本文共 15197 字,大约阅读时间需要 50 分钟。
Sysfs文件系统是一个类似于proc文件系统的特殊文件系统,用于将系统中的设备组织成层次结构,并向用户模式程序提供详细的内核数据结构信息。
去/sys看一看,
localhost:/sys#ls /sys/ block/ bus/ class/ devices/ firmware/ kernel/ module/ power/ Block目录:包含所有的块设备 Devices目录:包含系统所有的设备,并根据设备挂接的总线类型组织成层次结构 Bus目录:包含系统中所有的总线类型 Drivers目录:包括内核中所有已注册的设备驱动程序 Class目录:系统中的设备类型(如网卡设备,声卡设备等)sys下面的目录和文件反映了整台机器的系统状况。比如bus,
localhost:/sys/bus#ls i2c/ ide/ pci/ pci express/ platform/ pnp/ scsi/ serio/ usb/ 里面就包含了系统用到的一系列总线,比如pci, ide, scsi, usb等等。比如你可以在usb文件夹中发现你使用的U盘,USB鼠标的信息。我们要讨论一个文件系统,首先要知道这个文件系统的信息来源在哪里。所谓信息来源是指文件组织存放的地点。比如,我们挂载一个分区,
mount -t vfat /dev/hda2 /mnt/C
我们就知道挂载在/mnt/C下的是一个vfat类型的文件系统,它的信息来源是在第一块硬盘的第2个分区。
但是,你可能根本没有去关心过sysfs的挂载过程,她是这样被挂载的。
mount -t sysfs sysfs /sys
ms看不出她的信息来源在哪。sysfs是一个特殊文件系统,并没有一个实际存放文件的介质。断电后就玩完了。简而言之,sysfs的信息来源是kobject层次结构,读一个sysfs文件,就是动态的从kobject结构提取信息,生成文件。
所以,首先,我要先讲一讲sysfs文件系统的信息来源 -- kobject层次结构。kobject层次结构就是linux的设备模型。
莫愁前路无知己,天下谁人不识君。 唐·高适· 别董大
关于linux设备模型网上有一些论述,有些东西我就用了拿来主义,进行了修改和整理。
§1 Kobject
Kobject 是Linux 2.6引入的新的设备管理机制,在内核中由struct kobject表示。通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。Kobject是组成设备模型的基本结构。类似于C++中的基类,它嵌入于更大的对象的对象中--所谓的容器--用来描述设备模型的组件。如bus,devices, drivers 都是典型的容器。这些容器就是通过kobject连接起来了,形成了一个树状结构。这个树状结构就与/sys向对应。 kobject 结构为一些大的数据结构和子系统提供了基本的对象管理,避免了类似机能的重复实现。这些机能包括 - 对象引用计数. - 维护对象链表(集合). - 对象上锁. - 在用户空间的表示. Kobject结构定义为: struct kobject { char * k name; 指向设备名称的指针 char name[KOBJ NAME LEN]; 设备名称 struct kref kref; 对象引用计数 struct list head entry; 挂接到所在kset中去的单元 struct kobject * parent; 指向父对象的指针 struct kset * kset; 所属kset的指针 struct kobj type * ktype; 指向其对象类型描述符的指针 struct dentry * dentry; sysfs文件系统中与该对象对应的文件节点路径指针 }; |
相关函数
void kobject_init(struct kobject * kobj);kobject初始化函数。 int kobject_set_name(struct kobject *kobj, const char *format, ...);设置指定kobject的名称。 struct kobject *kobject_get(struct kobject *kobj);将kobj 对象的引用计数加1,同时返回该对象的指针。 void kobject_put(struct kobject * kobj); 将kobj对象的引用计数减1,如果引用计数降为0,则调用kobject release()释放该kobject对象。 int kobject_add(struct kobject * kobj);将kobj对象加入Linux设备层次。挂接该kobject对象到 |
struct kobj_type { void (*release)(struct kobject *); struct sysfs_ops * sysfs_ops; struct attribute ** default_attrs; }; |
attribute struct attribute { char * name; struct module * owner; mode_t mode; }; |
struct kset { struct subsystem * subsys; 所在的subsystem的指针 struct kobj type * ktype; 指向该kset对象类型描述符的指针 struct list head list; 用于连接该kset中所有kobject的链表头 struct kobject kobj; 嵌入的kobject struct kset hotplug ops * hotplug ops; 指向热插拔操作表的指针 }; |
这幅图很经典,她反映了整个kobject的连接情况。
相关函数
与kobject 相似,kset_init()完成指定kset的初始化,kset_get()和kset_put()分别增加和减少kset对象的引用计数。 Kset_add()和kset_del()函数分别实现将指定keset对象加入设备层次和从其中删除;kset_register()函数完成 kset的注册而kset_unregister()函数则完成kset的注销。§4 subsystem
如果說kset 是管理kobject 的集合,同理,subsystem 就是管理kset 的集合。它描述系统中某一类设备子系统,如block subsys表示所有的块设备,对应于sysfs文件系统中的block目录。类似的,devices subsys对应于sysfs中的devices目录,描述系统中所有的设备。Subsystem由struct subsystem数据结构描述,定义为: struct subsystem { struct kset kset; 内嵌的kset对象 struct rw semaphore rwsem; 互斥访问信号量 }; |
可以看出,subsystem与kset的区别就是多了一个信号量,所以在后来的代码中,subsystem已经完全被kset取缔了。
每个kset属于某个subsystem,通过设置kset结构中的subsys域指向指定的subsystem可以将一个kset加入到该subsystem。所有挂接到同一subsystem的kset共享同一个rwsem信号量,用于同步访问kset中的链表。
相关函数 subsystem有一组类似的函数,分别是: void subsystem_init(struct subsystem *subsys); int subsystem_register(struct subsystem *subsys); void subsystem_unregister(struct subsystem *subsys); struct subsystem *subsys_get(struct subsystem *subsys) void subsys_put(struct subsystem *subsys); |
关于那些函数的用法,会在后面的举例中详细讲。这里仅仅是一个介绍。
§1 bus
系统中总线由struct bus_type描述,定义为: struct bus_type { char * name; 总线类型的名称 struct subsystem subsys; 与该总线相关的subsystem struct kset drivers; 所有与该总线相关的驱动程序集合 struct kset devices; 所有挂接在该总线上的设备集合 struct bus attribute * bus_attrs; 总线属性 struct device attribute * dev_attrs; 设备属性 struct driver attribute * drv_attrs; 驱动程序属性 int (*match)(struct device * dev, struct device_driver * drv); int (*hotplug) (struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size); int (*suspend)(struct device * dev, u32 state); int (*resume)(struct device * dev); }; |
§2 device
系统中的任一设备在设备模型中都由一个device对象描述,其对应的数据结构struct device 定义为: struct device { struct list_head g_list; struct list_head node; struct list_head bus_list; struct list_head driver_list; struct list_head children; struct device *parent; struct kobject kobj; char bus_id[BUS_ID_SIZE]; struct bus_type *bus; struct device_driver *driver; void *driver_data; /* Several fields omitted */ }; |
struct device_driver { char *name; 设备驱动程序的名称 struct bus_type *bus; 该驱动所管理的设备挂接的总线类型 struct kobject kobj; 内嵌kobject对象 struct list_head devices; 该驱动所管理的设备链表头 int (*probe)(struct device *dev); 指向设备探测函数,用于探测设备是否可以被该驱动程序管理 int (*remove)(struct device *dev); 用于删除设备的函数 /* some fields omitted*/ }; |
可能你面对刚刚列举出来的一些列数据结构,感到很苦恼,很莫名其妙。没关系,我接下来讲个例子您就明白了。
对了,你得把ldd3的examples代码下下来。不然没法继续了。
接下来我们从例子着手,
localhost:/home/XX/examples/lddbus#insmod lddbus.ko 此时再看/sys/bus/ 这时就多了一个文件夹ldd。里面的文件构成是这样的 /sys/bus/ldd/ |--device |--driver `--version localhost:/sys/bus/ldd#cat version $Revision: 1.9$这表示系统中多了一种名叫ldd的总线类型。同时再看/sys/device/,也多出来一个ldd0的文件夹。这表示系统中多了一个名叫ldd0的硬件。
在lddbus.c中, 定义了一个总线和硬件类型 struct bus_type ldd_bus_type = { .name = "ldd", .match = ldd_match, .hotplug = ldd_hotplug, }; struct device ldd_bus = { .bus_id = "ldd0", .release = ldd_bus_release }; |
lddbus模块初始化时调用这个函数
static int __init ldd_bus_init(void) { int ret; ret = bus_register(&ldd_bus_type); if (ret) return ret; if (bus_create_file(&ldd_bus_type, &bus_attr_version)) printk(KERN_NOTICE "Unable to create version attribute\n"); ret = device_register(&ldd_bus); if (ret) printk(KERN_NOTICE "Unable to register ldd0\n"); return ret; } |
其实就是调用了两个注册函数,bus_register(), device_register()。bus_create_file()是在sysfs下创建一个文件夹。
bus_register(),向系统注册ldd_bus_type这个总线类型。bus_create_file()这个就是向sysfs中创建一个文件。device_register()系统注册ldd_bus这个硬件类型。
注册好了之后,我们就可以在sysfs下看到相应的信息。我们深入下去,仔细看看bus_register的代码。
688 int bus_register(struct bus_type * bus) 689 { 690 int retval; 691 692 retval = kobject_set_name(&bus->subsys.kset.kobj, "%s", bus->name); 693 if (retval) 694 goto out; 695 696 subsys_set_kset(bus, bus_subsys); 697 retval = subsystem_register(&bus->subsys); 698 if (retval) 699 goto out; 700 701 kobject_set_name(&bus->devices.kobj, "devices"); 702 bus->devices.subsys = &bus->subsys; 703 retval = kset_register(&bus->devices); 704 if (retval) 705 goto bus_devices_fail; 706 707 kobject_set_name(&bus->drivers.kobj, "drivers"); 708 bus->drivers.subsys = &bus->subsys; 709 bus->drivers.ktype = &ktype_driver; 710 retval = kset_register(&bus->drivers); 711 if (retval) 712 goto bus_drivers_fail; 713 bus_add_attrs(bus); 714 715 pr_debug("bus type '%s' registered\n", bus->name); 716 return 0; 717 718 bus_drivers_fail: 719 kset_unregister(&bus->devices); 720 bus_devices_fail: 721 subsystem_unregister(&bus->subsys); 722 out: 723 return retval; 724 } |
692-700是对bus->subsys的操作。701-705是操作bus->devices。706-710是操作bus->drivers。
692 kobject_set_name()设置bus->subsys.kset.kobj的名字。此函数很简单,就是调用vsnprintf()。此不列出。
696 subsys_set_kset(bus, bus subsys)
#define subsys_set_kset(obj,_subsys) (obj)->subsys.kset.kobj.kset = &(_subsys).kset
我们先看看bus_subsys的定义,它是一个subsystem类型的全局变量。在driver/base/bus.c中,decl subsys(bus, &ktype bus, NULL); 在/include/linux/kobject.h中有,decl subsys的原型,
#define decl_subsys(_name,_type,_hotplug_ops) \ struct subsystem _name##_subsys = { \ .kset = { \ .kobj = { .name = __stringify(_name) }, \ .ktype = _type, \ .hotplug_ops =_hotplug_ops, \ } \ } |
struct subsystem bus_subsys = { \ .kset = { \ .kobj = { .name = "bus" }, \ .ktype = ktype_bus, \ .hotplug_ops =NULL, \ } \ } |
static struct kobj_type ktype_bus = { .sysfs_ops = &bus_sysfs_ops, }; |
697 subsystem_register(&bus->subsys)作用是向全局的bus_subsys"登记", 把自己加入到bus_subsys的链表中去。
subsystem_register() -> kset_add() -> kobject_add() 155 int kobject_add(struct kobject * kobj) 156 { 157 int error = 0; 158 struct kobject * parent; 159 160 if (!(kobj = kobject_get(kobj))) 161 return -ENOENT; 162 if (!kobj->k_name) 163 kobj->k_name = kobj->name; 164 parent = kobject_get(kobj->parent); 165 166 pr_debug("kobject %s: registering. parent: %s, set: %s\n", 167 kobject_name(kobj), parent ? kobject_name(parent) : "<NULL>", 168 kobj->kset ? kobj->kset->kobj.name : "<NULL>" ); 169 170 if (kobj->kset) { 171 down_write(&kobj->kset->subsys->rwsem); 172 173 if (!parent) 174 parent = kobject_get(&kobj->kset->kobj); 175 176 list_add_tail(&kobj->entry,&kobj->kset->list); 177 up_write(&kobj->kset->subsys->rwsem); 178 } 179 kobj->parent = parent; 180 181 error = create_dir(kobj); 182 if (error) { 183 /* unlink does the kobject_put() for us */ 184 unlink(kobj); 185 if (parent) 186 kobject_put(parent); 187 } else { 188 kobject_hotplug(kobj, KOBJ_ADD); 189 } 190 191 return error; 192 } |
kset_register(&bus->devices) 和kset_register(&bus->drivers)作用类似,把bus->devices这个kset加入到 bus->subsys这个subsystem中去。最后形成图1的层次结构。
图1:lddbus kobject层次结构
同理,我们可以看看device_register()的代码,它也是向devices_subsys这个subsystem注册,最后形成这样的结构与图1类似。
目前为止,我们知道了所谓的xx_register函数,就是通过其内嵌的kobject链入对应的subsystem,或是kset的层次结构中去。这样就可以通过一些全局的变量找到它们了。
不妨再把sculld的代码也分析一下,先看初始函数
sculld_init() -> register_ldd_driver() ->driver_register() ->bus_add_driver() -> register_ldd_device() ->device_register() ->device_add() ->kobject_add() ->bus_add_device() |
首先注册驱动,看bus_add_driver()
532 int bus_add_driver(struct device_driver * drv) 533 { 534 struct bus_type * bus = get_bus(drv->bus); 535 int error = 0; 536 537 if (bus) { 538 pr_debug("bus %s: add driver %s\n", bus->name, drv->name); 539 error = kobject_set_name(&drv->kobj, "%s", drv->name); 540 if (error) { 541 put_bus(bus); 542 return error; 543 } 544 drv->kobj.kset = &bus->drivers; 545 if ((error = kobject_register(&drv->kobj))) { 546 put_bus(bus); 547 return error; 548 } 549 550 down_write(&bus->subsys.rwsem); 551 driver_attach(drv); 552 up_write(&bus->subsys.rwsem); 553 module_add_driver(drv->owner, drv); 554 555 driver_add_attrs(bus, drv); 556 } 557 return error; 558 } 559 |
353 void driver_attach(struct device_driver * drv) 354 { 355 struct bus_type * bus = drv->bus; 356 struct list_head * entry; 357 int error; 358 359 if (!bus->match) 360 return; 361 362 list_for_each(entry, &bus->devices.list) { 363 struct device * dev = container_of(entry, struct device, bus_list); 364 if (!dev->driver) { 365 error = driver_probe_device(drv, dev); 366 if (error && (error != -ENODEV)) 367 /* driver matched but the probe failed */ 368 printk(KERN_WARNING 369 "%s: probe of %s failed with error %d\n", 370 drv->name, dev->bus_id, error); 371 } 372 } 373 } |
455 int bus_add_device(struct device * dev) 456 { 457 struct bus_type * bus = get_bus(dev->bus); 458 int error = 0; 459 460 if (bus) { 461 down_write(&dev->bus->subsys.rwsem); 462 pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id); 463 list_add_tail(&dev->bus_list, &dev->bus->devices.list); 464 465 up_write(&dev->bus->subsys.rwsem); 466 device_add_attrs(bus, dev); 467 sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id); 468 } 469 return error; 470 } |
最后形成的kobject层次结构如图所示。
变化
计划赶不上变化,当前的内核版本已经是2.6.22了,其中不少数据结构作了变动,而且subsystem这个数据结构已经没有了,完全被kset取缔了。但是原理上并没有变,我认为,学习知识是一方面,更重要的是学习方法。只要懂了方法,我们才可"以不变应万变"。17大马上要召开了,刘翔又夺冠了,奥尔默特与阿巴斯也会面了,明年就奥运了。和谐的社会里充满着希望与绝望。不管怎样,终于把设备模型介绍完毕,接下来进入sysfs部分。
人面不知何处去,桃花依旧笑春风。 唐·崔护·题都城南庄
接下来,我们进入sysfs部分。看看
kobject_add()->create_dir()->sysfs_create_dir() bus create file->sysfs create file() . . . 这些sysfs函数的内幕。 说白了,sysfs就是利用VFS的接口去读写kobject的层次结构,建立起来的文件系统。关于sysfs的内容就在fs/sysfs/下。 kobject的层次结构的更新与删除就是那些乱七八糟的XX_register()们干的事情。在kobject_add()里面,调用了sysfs_create_dir()。让我们看看它究竟是如何create的。
135 int sysfs_create_dir(struct kobject * kobj) 136 { 137 struct dentry * dentry = NULL; 138 struct dentry * parent; 139 int error = 0; 140 141 BUG_ON(!kobj); 142 143 if (kobj->parent) 144 parent = kobj->parent->dentry; 145 else if (sysfs_mount && sysfs_mount->mnt_sb) 146 parent = sysfs_mount->mnt_sb->s_root; 147 else 148 return -EFAULT; 149 150 error = create_dir(kobj,parent,kobject_name(kobj),&dentry); 151 if (!error) 152 kobj->dentry = dentry; 153 return error; 154 } |
当你看见这么些新东西,如dentry出现的时候,你一定感到很困惑。诚然,我一度为代码中突然出现的事物感到恐慌,人类对未知的恐惧是与生俱来的,面对死亡,面对怪力乱神,我们抱着一颗敬畏的心灵就可以了。而面对linux,我们始终坚信,未知肯定是可以被探索出来的。
原文出处: http://blog.csdn.net/hongjiujing/archive/2009/09/07/4528070.aspx
http://blog.163.com/hbu_lijian/blog/static/126129153200911521431114/
转载地址:https://ciellee.blog.csdn.net/article/details/82599486 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!