Chinaunix首页 | 论坛 | 博客
  • 博客访问: 227669
  • 博文数量: 49
  • 博客积分: 2101
  • 博客等级: 大尉
  • 技术积分: 525
  • 用 户 组: 普通用户
  • 注册时间: 2010-09-07 10:38
文章分类

全部博文(49)

文章存档

2010年(49)

我的朋友

分类: 嵌入式

2010-10-05 12:17:02

Linux设备模型与sysfs文件系统

1 前言

学习设备模型是因为我学习usb驱动而引起的。在看《linux设备驱动》的第13usb驱动程序,发现有点进行不下去,必须先看明白设备模型才行。于是跳到第14linux设备模型。第14章,我看了两周,断断续续的看,总也理解不透彻。期间在网上看到一篇《linux设备模型深探》博客文章,有所启发。而后翻看了《linux内核设计与实现》第17章,才更加明白。今天一看《深入理解linux内核》第13章讲解设备模型部分,感觉理解更加透彻。

说的linux的设备模型,就不得不说sysfs文件系统,这是一个处于内存中虚拟文件系统,为设备模型提供类似文件系统的视图,也就是说sysfs只是设备模型的一种表象。但是要注意区分设备模型和sysfs两个概念。

根据经验,学习设备模型与sysfs的顺序应该是:

linux内核设计与实现》第17章;

《深入理解linux内核》第13章;

linux设备驱动》的第14章。

 

1 kobject kset subsystem

1.1 kobject

         设备模型的核心数据是kobject数据结构。《linux内核设计与实现》中如下表述:kobject类似于C#Java这些面向对象语言中的object对象类,提供了诸如引用计数、名称和父指针等字段,可以创建对象的层次结构。因此,设备模型中的任一对象都包含一个kobject对象,或者说kobject总是被嵌入到一个叫做“容器”的更大对象中去。容器的典型例子就是某种设备的抽象。例如字符设备驱动中会涉及到cdev数据结构,cdev用于描述一个字符设备,而cdev中就定义了(嵌入了)一个kobject对象。当然再后面会看到容器还可以是总线、驱动等等。任何逻辑概念都可以作为容器,包括后面介绍的kset数据结构。

         下面介绍一下kobject的数据结构内容(摘自linux2.6.21内核源码,下同):

struct kobject {

         const char                  * k_name;

         char                    name[KOBJ_NAME_LEN];

         struct kref                  kref;

         struct list_head        entry;

         struct kobject           * parent;

         struct kset                 * kset;

         struct kobj_type       * ktype;

         struct dentry             * dentry;

         wait_queue_head_t         poll;

};

         kref是容器的引用计数器;entry是为了kobject的链表操作(不明白的参考ldd3的第11章的链表部分);parent就很明了了,它指向一个父kobject对象,用于构造设备模型中的层次关系;kset用于构造更复杂的层次关系,后面会介绍;dentry指向了与kobject对于的sysfs文件系统中的dentry数据结构,也正是利用dentry才得以形成sysfs虚拟文件系统。关于dentry需要学习linuxVFSpoll目前没找到什么解释。

         最后重点介绍ktype数据结构。从kboject结构可以看出,它利用parent字段可以构成父子层次关系;但是设备模型除了描述容器的层次关系,还要描述容器的特性,而ktype数据结构就是为此定义的。ktype数据结构如下。

struct kobj_type {

         void (*release)(struct kobject *);

         struct sysfs_ops       * sysfs_ops;

         struct attribute        ** default_attrs;

};

         release函数指针指向在kobject引用计数减至零时要被调用的析构函数。该函数负责释放所有的kobject使用的内存和其他相关清理工作。析构函数的概念是从《linux内核设计与实现》中看来的,因为我C++C#都还学的不错,所有对这个概念很敏感,觉得总结的非常好。

         default_attributesysfs_ops是配合使用的。default_attribute指向一个包含attribute结构数组。attribute结构如下。

struct attribute {

         const char                  * name;

         struct module                   * owner;

         mode_t                       mode;

};

         name是属性的名字;owner指向模块指针;mode是属性的保护位,设置读写权限等。

         attribute可以说明容器具有哪些属性,而sysfs_ops中的函数指针所指向的函数就是来操作(读/写)这些的属性的。sysfs_ops数据结构定义如下。

struct sysfs_ops {

         ssize_t     (*show)(struct kobject *, struct attribute *,char *);

         ssize_t     (*store)(struct kobject *,struct attribute *,const char *, size_t);

};

         从数据结构中可以看到,定义了showstroe分别来读写容器的属性。

         综上所述,kobject作为基类对象,总是被嵌入一个叫做“容器”的更大的对象中去;它使用kref完成引用计数;利用parent可以实现父子层次关系;利用ktype描述容器的属性。但是,kset用来做什么呢?因为parent所实现的父子层次关系是有缺陷的,所以需要kset

 

1.2 kset

1.2.1 ksetkobject

         在讨论kset之前,先来看看kobjectparent字段所构成的父子层次关系为什么有缺陷。注意parent实现的父子层次关系是单向的,因此一个kobject仅仅知道其父kobject,却不知道其子kobject。并且,一个kobject有且只有一个父kobject,但是可能会有多个子kobject

         如果让我来解决这个问题,我就在kobject中定义一个kobjcet集合,该集合用双向链表来把所有的子kobject链接起来。这样,每一个容器中嵌入一个kobject,每一个kobject通过kobject集合可以访问所有的子kobject;通过parent可以访问父kobject。我最新设计的VT100菜单库就是使用这种结构的。我的每个菜单项都包含一个kobject,包括顶层菜单项同样是包含一个kobjectkobject中的kobject集合中就是子菜单项,所有子菜单项中的kobject的父kobject都指向父菜单中的kobject;直至最后一级子菜单中,其kobjcet集合为空。

         带着上述思路,我原本理解出来的kobjectkset构成的层次结构是这样的。

         注意,kobjectkset中都有list_head,用于形成双向链表。

         但是,在读三本参考文献时,我发现我的上述理解是错误的。linux的设备模型不是我想的这样子的。首先看一下kset的数据结构定义。

struct kset {

         struct subsystem     * subsys;

         struct kobj_type       * ktype;

         struct list_head        list;

         spinlock_t                  list_lock;

         struct kobject           kobj;

         struct kset_uevent_ops  * uevent_ops;

};

         kset中包含list_head,说明其可以用于构成双向链表;它还嵌入了一个kobject,表明了kset其实是一个嵌入了kobject对象的容器。理解这一点很关键,回想kobject的表述,kobject总是被嵌入到一个更大的称为“容器”的对象中去,当时举了cdev的例子,而现在kset也是一个例子。但是kset比较特别,它内部不但嵌入了一个kobject,还包含了kobject的集合。如何实现这个kobject集合呢,是使用kset中的list_head和其所包含的kobject中的list_head形成双向链表。

         ksetkobject的关系,在ldd3的第14章有一个经典的图。

         图中,灰色部分表示kset数据结构,作为一个容器,其内部嵌入了一个koject对象。而底端三个kobject是嵌入到其他容器中的,这些kobjectparent就是kset中的kobject,从黑色虚线能看出来;而灰色虚线则表示底端kobject中的kset指针指向灰色的kset。黑色实线表明kset中的list_head以及底端kobject中的kobjectlist_head构成了双向链表。

         上图虽然经典,但是只能描述ksetkobjects间的单层关系。在我看这幅图时,我总是会有这样的疑问:底端的kobjects的容器会不会也是一个kset呢?会不会出现多个kset层次呢?从编程角度来说,是完全可以这样的;但是答案是否定的,因为linux不是这么用的。在设备模型树中,任一路径中,有且只能有一个“单一kset”。这个结论是我自己得出来的,后面会解释我为什么得出这个结论。

         综上所述,ksetkobject的关系就很明了了。kset是嵌入了kobject的一个特殊容器,它利用list_head实现了子kobject的集合。

1.2.2 ksetktype

         kset做为一个kobject的容器,本来是要使用kobject中的ktype的;但是kset也定义了一个ktypeldd3给出了明确的解释:kset中的ktype也用来描述其包含的kobject,优于kobject中的ktype使用。在典型应用中,kobject中的ktype设为null

1.2.3 ksetsubsystem

         kset中包含一个subsystem指针;在linux中,每一个kset都必须属于一个子系统。

1.3 subsystem

         subsystem的数据结构很简单。

struct subsystem {

         struct kset                 kset;

         struct rw_semaphore      rwsem;

};

         subsystem结构中明显嵌入了一个kset结构;因此可以将subsystem看做是kset的容器。但是因为kset中嵌入了一个kobject,所以可以说subsystem中也是一个kobject的容器。

         subsystem的作用是归纳kset集合。在subsystem的底层,会出现如下两种情况:第一,其底层是多个subsystem;第二种,其底层就是kset。无论是那种情况,以kset的能力都是能有效组织起来的。

         linux中的设备模型的顶层是从subsystem子系统开始的。在sys目录底下能看到,其底下包括block_subsys(/sys/block)devices_subsys/sys/devices)等子系统。但是子系统subsytem不一定出现在顶层,也有可能出现在子系统中。在上面已经提到过,kset必须属于一个子系统;在像内核中添加一个kset结构时,如果没有指定子系统,那么这个kset将会出现在顶层。因此,在上面我得出过结论:在设备模型树中,任一路径中“单一kset”只会出现一次。请参考《深入理解linux内核》中的一幅图(图13-3),就能基本能理解设备模型了。

1.4 关于设备模型的若干结论

         kobject总是被嵌入到一个称为“容器”的更大的对象中;单独存在的kobject是没意义的;

         kset是一个嵌入了kobject的特殊容器,它内部包含一个子kobjects的集合;kset的上层必须是subsystemkset的下层必然是kobject;在设备模型树中,任一路径中“单一kset”只会出现一次。

         subsystemkset的集合,它的底层可以是kset也可以是subsystem

 

2 sysfs文件系统

         sysfs的诀窍是把kobject对象与目录项(dentry)紧密联系起来,这点事通过kobject对象中的dentry实现的。对于sysfs中的每个目录,都会有一个kobject与其对应。

         sysfs中我们看到的是目录树,它是对象模型树导出而形成的文件系统。

         sysfs下目前有7个子系统目录。

block

每个目录对应着系统中的一个块设备,独立于所连接的总线

devices

所有设备拓扑结构视图,依照连接它们的总线进行组织

bus

系统中用于连接设备的总线

drivers

在内核中注册的设备驱动程序

class

以高层逻辑组织起来的系统设备视图,如声卡、显卡、网卡等;同一类可能包含由不同总线连接的设备。

power

处理一些硬件设备电源的文件

firmware

包含硬件设备的固件,诸如ACPI/EDD/EFI等低层子系统的特殊树

         对于上述子系统,ldd3重点讲解了busdevicesdrivers三个子系统。上面说了这么多都是虚的,看看这三个子系统在sysfs中具体实现才能够更加明确设备模型的结构。

         linux设备模型中,用bus_type结构表示总线。在sysfs中可以看到,bus_type定义的总线子系统并不在sys顶层,而是位于sys/bus子系统中,如pciusb等总线子系统。bus_type中包含了一个subsystem,两个kset。这两个kset的父kset都是该subsystem中的kset。看/sys/bus/pci下能找到devicesdrives这两个kset。这两个kset就是单一kset

 

写在最后:下一步学习《总线、设备与驱动程序》的具体细节;然后就是热插拔的细节。

 

参考文献

linux内核设计与实现》第17章;

《深入理解linux内核》第13章;

linux设备驱动》的第14章。

 

 

阅读(2027) | 评论(0) | 转发(2) |
给主人留下些什么吧!~~