sysfs文件系统学习--sysfs

一、sysfs简介
1.sysfs就是利用VFS的接口去读写kobject的层次结构,建立起来的文件系统。其更新与删除是那些xxx_register()/unregister()做的事
情。从sysfs中读写就相当于从kobject层提取数据。每当新增一个kobject结构时,就会在sysfs中增加一个目录。kobject对应的是文件
夹,attribute对应的是该文件夹下的文件。它是一个简单的文件系统,不涉及任何硬件驱动。sysfs来源于设备的层次结构,读sysfs文件就是动态地从设备树中寻找相关节点提取信息,然后返回给用户。sysfs是用户
与内核的接口,也是用户与设备的接口,通过读写sysfs文件来查看和改变内核的配置。
2.设备模型支持功能如下:
    1)电源管理和系统关机。
    2)与用户空间通信。将控制接口通过文件的形式暴露给用户空间
    3)支持热插拔。
    4)设备分类。比如找USB鼠标直接去/sys/class/input/里面去找即可,而不用关心它是接在哪个USB控制器的那个hub的哪个端口上。
    5)对象的生命周期。比如U盘拔掉以后,全局设备树和sysfs里面得相应的去掉。
3.sysfs文件用户读show()被调用,用户写store()被调用。
4.sfsfs是一个虚拟的文件系统,没有存储介质,掉电丢失。
mount -t vfat /dev/sda2 /mnt :有存储介质
mount -t sysfs sysfs /sys :没有存储介质
5.fs/sysfs目录下bin.c file.c dir.c symlink.c 分别代表了在sysfs文件系统中当文件类型为二进制文件,普通文件,目录,符号链接时各自的file_operations的实现。

二、sysfs用户空间测试

/sys/bus/usb/drivers_autoprobe:
在bus_register() --> add_probe_files() --> bus_create_file()
属性:S_IWUSR|S_IRUGO
每次向总线注册设备/驱动时,都会通过klist_drivers/klist_devices去匹配驱动/设备,
但是也可以通过bus_register()中创建的这个文件在用户空间使能或禁止这种自动匹配!其实它改的就是:
直接只更改struct bus_type bus->p->drivers_autoprobe的值。

1./sys/bus/usb/drivers_probe:
在bus_register() --> add_probe_files() --> bus_create_file()
属性:S_IWUSR
store(): store_drivers_probe(),如果dev->driver为NULL,下面操作就会重新触发设备匹配驱动
# echo 2-1:1.0 > /sys/bus/usb/drivers_probe
# echo 2-2.1:1.0 > /sys/bus/usb/drivers_probe

插上U盘:
# echo 0 > /sys/bus/usb/drivers_autoprobe
# ls /sys/bus/usb/devices/ 对出 1-2
# echo 1-2 > /sys/bus/usb/drivers_probe 然后多出1-2:1.0
# echo 1-2:1.0 > /sys/bus/usb/drivers_probe 然后/dev/sdb1就产生了!

2./sys/bus/usb/uevent
在bus_register() --> bus_create_file()
属性:S_IWUSR
store(): bus_uevent_store(),
bus和drivers目录下有uevent文件,而devices目录下没有.
可以输入kobject_actions中的字符,由于向用户空间广播事件
# echo add > /sys/bus/usb/uevent
# echo bind > /sys/bus/usb/uevent
# echo online > /sys/bus/usb/uevent

/sys/bus/usb/drivers和devices目录:
在bus_register() --> kset_create_and_add("devices")/kset_create_and_add("drivers")

3.U盘设备文件操作为例:
1)设备和驱动的绑定
/sys/bus/usb/drivers/usb-storage/unbind
函数:unbind_store 属性S_IWUSR,找到设备,然后释放其驱动
echo 1-2:1.0 > /sys/bus/usb/drivers/usb-storage/unbind 然后U盘的设备文件/dev/sdb1就消失了

/sys/bus/usb/drivers/usb-storage/bind
函数:bind_store 属性S_IWUSR,找到设备,然后重新执行match操作
echo 1-2:1.0 > /sys/bus/usb/drivers/usb-storage/bind U盘的设备文件/dev/sdb1重新出现并且挂载了

2)使用动态id_table可以使id动态地增减,增加了灵活性
/sys/bus/usb/drivers/usb-storage/new_id
属性:S_IWUSR | S_IRUGO
函数:
new_id_show :仅仅是打印动态id的厂商设备接口类信息
new_id_store :命令行参数获取:sscanf(buf, "%x %x %x %x %x", &idVendor, &idProduct, &bInterfaceClass, &refVendor, &refProduct);
也可以只指定idVendor和idProduct,新创建的动态id挂载usb_driver.dynids链表上,然后调用driver_attach()匹配驱动
# echo 0951 01666 3 4 5 > /sys/bus/usb/drivers/usb-storage/new_id
# echo 0951 01666 > /sys/bus/usb/drivers/usb-storage/new_id
# cat /sys/bus/usb/drivers/usb-storage/new_id

/sys/bus/usb/drivers/usb-storage/remove_id
属性:S_IWUSR | S_IRUGO
函数:
remove_id_show :直接调用的是new_id_show,仅仅打印出可以remove的动态id
remove_id_store :就是直接将此id从usb_driver->dynids.list删除
# echo 0951 01666 > /sys/bus/usb/drivers/usb-storage/remove_id
# cat /sys/bus/usb/drivers/usb-storage/remove_id

三、设备的底层模型

1.kobject

struct kobject {
const char    *name; /*恒指向设备名称的指针*/
struct list_head    entry; /*使用它挂接在kset中*/
struct kobject    *parent; /*指向父对象(可能是kset.kobj)*/
struct kset    *kset; /*所属的kset,device_init()中指向为全局devices_kset*/
struct kobj_type    *ktype; /*指向其对象类型描述符的指针*/
struct kernfs_node    *sd; /* sysfs directory entry */
struct kref    kref; /*对象的引用计数,通过kobject_get()/kobject_put()访问*/
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work    release;
#endif
unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;
};

entry, parent, kset用来构成树状的结构的指针。相关函数有kobj_add() kobj_put()等

2.kset

struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;

一个kset里面的所有kobject组成一个双向循环链表,list是链表头。相同类型(kobj_type)的kobject集合在一起组成kset.
相关函数有kset_get() kset_put()等。

3.kobj_type

struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops; /*里面有show store*/
struct attribute **default_attrs; /*属性以文件的形式存储在sysfs中,文件名就是.name,*/
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};

attribute以文件的形式输出到sysfs的目录中,文件名就是name.kobject是连接attribute与sysfs的桥梁。文件读写对应于
kobj_type中的sysfs_ops,里面有show()和store()

kobject:
kobject_set_name(): 为kobj对象进行命名时里面的'/'将会被替换为'!'
kobject_del(): 会调用sysfs_remove_dir(kobj),删除一个目录也会先删除其所有的子目录或文件
kobject_add(): 会增加父目录各级kobject的引用计数,在其parent指向的目录下创建文件节点,并启动该类型内核对现的hotplug函数
kobject_add(): 会将kogj的引用计数减1,减为0后就会调用kobject_release()释放该对象。

kset:
kset_register(): 中调用了kobject_uevent(&k->kobj, KOBJ_ADD);
kset_unregister(): 只调用kobject_del(&k->kobj);kobject_put(&k->kobj);

struct device:
device_register()将一个device对象插入设备模型中,并在/sys/devices下创建一个对应的目录。
device_unregister()完成相反操作,注销设备。get_device()/put_device()分别增加减少引用计数。
它通常嵌入到其它结构体中使用

device_register() --> device_add() --> kobject_uevent(&dev->kobj, KOBJ_ADD);

四、设备模型的上层容器: bus、device、device_driver

1.总线bus是处理器与设备之间的通道。在设备模型中所有设备都是通过总线进行连接。即使设备没有在一根物理的总线上,也会设置
一个内部虚拟的平台总线来维持总线、设备、驱动的三角关系。
2.struct bus_type:每个bus_type对象都对应于/sys/bus目录下的一个子目录。使用bus_register()向系统中注册总线
3.device_register()将一个新的device对象插入设备模型中,并在/sys/devices/下创建对应的目录


五、代码阅读总结

1.file_operations dentry_operations inode_operations super_operations 实现一个文件系统需要实现这4个函数集合
file_operations:是对每一个具体的文件进行的读写操作
dentry_operations inode_operations 是对文件的属性进行更改,如更改名字,建立或删除操作。
2..对于不同的文件系统,open()系统调用调用到的是不同的file_operations.open()
3.进程通过目录项和索引节点描述文件
4.文件包含两方面的信息,存储的数据本身和有关该文件的组织和管理信息。每个文件都有一个dentry(目录项)和inode(索引节点,物理意义上的文件)。
dentry记录着文件名和上级目录信息等,形成树状结构。inode里面存放该文件的组织和管理信息。dentry和inode是一对多的关系,
因为可能一个文件有好几个文件名。
5.文件可分为:磁盘文件,设备文件,特殊文件
6.要打开/home/hello.c,先找到‘/’,读入其内容,找到名为'home'的文件的索引节点号,打开'/home'这个文件,找到名为‘hello.c’这个
文件的索引节点号,最后就得到‘/home/hello.c’了。
7.使用glibc进行读写的系统调用次数可能和读写调用的次数不一样!又封装了一下。
8.不管是bus还是设备添加到系统后都会调用kobject_uevent(&dev->kobj, KOBJ_ADD);
9.struct bus_type中有很多与struct driver重复的函数指针,这些函数在注册的时候只要bus的存在就使用bus的,bus的不存在才会使用
driver的,若两者都存在某些函数还会报警告!
10.module_add_driver(drv->owner, drv): usbstorge: /sys/bus/usb/drivers/usb-storage下创建到/sys/module的连接
11.创建sysfs文件:driver_create_file() --> sysfs_create_file(&drv->p->kobj, &attr->attr);
12.注册的bus和注册的驱动指向的bus可以是不是一个的,driver的bus用来为driver提供一些函数,好像driver并产生设备模型并没有涉及到bus
13.设备驱动绑定
bus_add_driver
如果 drv->probe_type == PROBE_PREFER_ASYNCHRONOUS, 则进行异步匹配
否则同步匹配,调用driver_attach()
bus_for_each_dev()
如果bus->match存在(usb_storage的是存在的),则调用bus的然后返回。如果不存在,调用really_probe
driver_bound()将设备加到驱动的链表中
kobject_uevent(&dev->kobj, KOBJ_BIND); 发送bind的事件,但是udevadm监听不到是因为没有在rules.d里面加吗?

原文地址:https://www.cnblogs.com/hellokitty2/p/9574790.html