sysfs原理

sysfs原理


1 sysfs简介

sysfs是一个in-memory的文件系统,是内核(对象)用来向用户空间传递信息的一个接口。sysfs的内部目录结构是很严格的基于kernel内部的数据结构的。在文件系统内部的所有文件一般都是ASCII文件,每一个值对应着一个文件。

sys中,提供了两个接口:一个kernel编程接口(Internal)kernel可以使用这个接口来将内核数据export到用户空间;另一个是用户接口(External),供用户观察和操作来自kernelexport出来的数据。下面是kernel内部数据结构与sysfs文件系统之间的映射关系:

  • Kernel ObjectsDirectoies(对应sys下的一个目录);

  • Object Atrributes: RegularFiles(对应sys下的一个文件,文件中值就是属性值);

  • Object Relationships:Symbolic Links(对应sys下的链接文件)。


2 sysfs的结构

ubuntu11.04(linux-2.6.38)的根目录下有一个文件夹叫做sys,这个就是sysfs


1 sysfs

  • bus –目录下的每个子目录都是kernel支持并已经注册的总线类型。一般来说每个子目录(总线类型)下包含两个目录:一个是devices,另外一个是driversdevices下是这个总线类型下的所有设备,这些设备都是符号链接他们分别指向真正的设备/sys/devices/xxx;drivers下是所有注册在这个总线上的driver,每个driver子目录下是一些可以观察和修改的driver参数。在内核中对应的结构体为struct bus_type { … };

  • device -该目录是全局设备结构体系,他包含所有被发现的注册在各种总线上的各种物理设备。一般来说所有的物理设备都按其在总线上的拓扑结构来显示,但这里有两个例外,就是platform devicessystem devicesplatform devices一般是挂在芯片内部高速或者低速总线上的各种控制器和外设,他们能被CPU直接寻址。system devices不是外设,他是芯片内部的核心结构,比如CPUtimer等,他们一般没有相关的driver,但是会有一些体系结构相关的代码来配置他们。对应的结构体为struct device { … };

  • class -系统中设备的类型(声卡,网卡,显卡,输入设备等),每一个设备类型表达具有一种功能的设备。每个设备类型子目录下都是这种设备类型的各种设备的符号链接,这些链接指向/sys/devices/xxx下的具体设备。设备类型和设备没有一一对应的关系,一个物理设备可能具备几种设备类型。同一类中包含的设备可能连接到不同的总线,对应的结构体为struct class { … };

  • dev -在内核中注册的设备驱动程序,对应的结构体为struct device_driver{ … };

以上busdeviceclassdev是可感受到的对象,在内核中都用相应的结构体来描述。而实际上,按照面向对象的思想,程序需要抽象出一个最基本的对象,这就是设备模型的核心对象kobject


3 挂载sysfs

sysfs可以像其他的memorybased文件系统一样在用户空间被mount
$mount -t sysfs sysfs/sys
sysfs
也可以放在/etc/fstab里面在boot的时候自动被mount,大多数基于linux发行版在/etc/fstab里面有sysfs的注册:
sysfs /sys sysfs noauto 0 0


4 sysfs的代码组织

sysfs所有的代码都在内核目录/fs/sysfs/里面,它还有一个公共的函数接口定义在内核目录/include/linux/sysfs.h,下面是内核目录/fs/sysfs/里面的具体内容:

  • sysfs.hsysfs的内部头文件,包含一些供本地调用的函数声明;

  • mount.c:包含一些和VFS进行交互的数据结构,函数,以及必要的初始化函数;

  • inode.c:这个文件包含了一些共享的内部函数,用来申请和释放核心的文件系统的objects

  • dir.c:这个文件包含一些在sysfs体系结构中创建和删除目录的接口;

  • file.c:这个文件包含了一些在sysfs体系结构中创建和删除常规ASCII文件的接口;

  • group.c:这个文件包含了一些在sysfs中同时创建多个常规文件的帮助;

  • symlink.c:这个文件包含一些在sysfs体系结构中创建和删除符号链接的接口;

  • bin.c:这个文件包含了一些在sysfs体系结构中创建和删除二进制文件的接口。

Kobject的所有代码都在内核目录/lib/kobject.c里面,头文件在内核目录/include/linux/kobject.h


5 sysfs的初始化

sysfs使用内核目录/fs/sysfs/mount.c里面的sysfs_init函数来进行初始化,这个函数被vfs初始化代码直接调用,因为很多依赖sysfs的子系统在初始化的时候都需要在sysfs里面注册object,所以sysfs的初始化一定要尽早。这个初始化过程主要完成3件事:(1)创建一个kmem_cache,这个cache主要用来分配sysfs_dirent结构体的对象;
(2)
VFS里面注册,使用register_filesystem()来注册一个sysfs_fs_type类型的文件系统,并命名为sysfs;
(3)
在内部将sysfs挂载上,这是为了确保sysfs对其他kernel code是一直有效的,甚至在启动的过程中,而不需要用户显示的来mount

一旦上面的三个步骤完成,sysfs的功能就全部起来了,可以被其他的内部代码使用了。


6 sysfs的配置

sysfs默认会被编译进kernel,主要依赖于CONFIG_SYSFS这个选项。CONFIG_SYSFS只有在CONFIG_EMBEDDED被设置的情况下才是可见的。


7 kernel相关接口

sysfs的对象(object)可分为3种:

Kernel Objects (Directoriessysfs中以目录的形式出现)

Object Attributes (Regular Filessysfs中以文件的形式出现)

Object Relationships (Symbolic Linkssysfs中以链接的形式出现)

另外还有两种用来导出属性到用户空间的子类型:

Attribute Groups

Binary Files

这两个子类型主要是为了使用户导出其他文件更为简单,这两种类型都会在sysfs里面创建常规文件。对所有的sysfs接口函数,第一个参数都是kobjectsysfs假定在函数调用过程中kobject一直都是有效的,调用者必须确认在调用过程中是否存在可能的锁会修改object的内容。对于基本上所有的接口函数(sysfs_create_dir除外)sysfs都假定kobj->sd是一个有效的目录项,并且已经分配空间并初始化。所有对sysfs接口函数的调用都必须在进程上下文中,而且不能在获得自旋锁的情况下对其进行调用,因为对这些函数的调用可能会导致sleep,所以一般都使用信号量。


8 Linux底层原理

8.1 sysfs目录、文件创建原理

内核目录/fs/sysfs/dir.c定义了与目录操作有关的函数,例如sysfs_create_dir是用来创建目录的,sysfs_remove_dir则是用来删除目录的,在sysfs中,目录表示一个内核对象(kobject),因此这些函数都会有一个struct kobject的指针参数。同理,在内核目录/fs/sysfs/file.c定义了与文件操作相关的函数,例如sysfs_create_file是用来建立文件的,sysfs_remove_file是用来删除文件的,这些函数也需要一个struct kobject指针参数,因为在sysfs下,文件代表了目录(内核对象)的属性。

8.2 相关函数举例

下面以几个sysfs函数为例来说明。

8.1.1 sysfs_create_dir

该函数在内核目录/fs/sysfs/dir.c中定义为:

/**

* sysfs_create_dir - createa directory for an object.

* @kobj: objectwe're creating directory for.

*/

intsysfs_create_dir(struct kobject * kobj);

该函数会在/sys下建立文件夹,由kobj参数来决定目录的位置以及名字,目录会建立在kobj->parent的目录下面, kobj->parent可以为空,这样目录就会建立在sysfs的顶层目录下。程序应该避免在顶层目录下建立目录,除非已实现或者移植一个新的顶层子系统。该函数会为要创建的目录分配一个kobj->sd(struct sysfs_dirent),这个sd用于VFS的各种操作。这样一个用户可见的节点就被创建了,sysfs会根据内部实现为这个新目录填充需要的file_operations,用来供vfs标准函数调用。


8.1.2 sysfs_remove_dir

该函数在内核目录/fs/sysfs/dir.c中定义为:

voidsysfs_remove_dir(struct kobject * kobj)

该函数会在/sys下删除文件夹和其中的文件(属性),由kobj参数来决定目录的位置以及名字。


8.1.3 sysfs_rename_dir

该函数在内核目录/fs/sysfs/dir.c中定义为:

int sysfs_rename_dir(struct kobject *kobj, const char *new_name)

该函数会用来对目录重新命名


8.2 kobject

kobject通过sysfs以目录的方式来导出到用户空间(在/sys/下以目录的形式出现),该结构体在内核目录/include/linux/kobject.h中定义:

[cpp] view plain copy print ?
  1. struct kobject {  
  2.     const char *name;  
  3.     struct list_head    entry;  
  4.     struct kobject *parent;  
  5.     struct kset *kset;  
  6.     struct kobj_type    *ktype;  
  7.     struct sysfs_dirent *sd;  
  8.     struct kref kref;  
  9.     unsigned int state_initialized:1;  
  10.     unsigned int state_in_sysfs:1;  
  11.     unsigned int state_add_uevent_sent:1;  
  12.     unsigned int state_remove_uevent_sent:1;  
  13.     unsigned int uevent_suppress:1;  
  14. };  

8.2.1 kobject的初始化

该过程比较复杂,但是必须的步骤如下:

将整个kobject清零,通常使用memset函数。

调用kobject_init函数。kobject_init函数还会调用kobject_init_internal函数,设置kobject的引用计数为1

调用kobject_add函数将kobj对象加入Linux设备层次。


8.2.2kobject有关的函数:

(1) kobject_init

该函数为kobject的初始化函数,它在内核目录/lib/kobject.c中被定义为:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

该函数参数是一个已经创建了的kobject对象的地址和该对象对应的kobj_type,该函数会将kobject对象的引用计数初始化为1kref_init(&kobj->kref);),此外,kobj_type包含了kobject被释放时需要调用的函数以及属性文件列表,以及这些文件的读写函数。

(2) kobject_set_name

该函数在内核目录/lib/kobject.c中被定义为:

int kobject_set_name(struct kobject *kobj, const char *format, ...);

主要功能是设置指定kobject的名称,即name成员,参数format表示格式化的字符串,与printf的格式化字符串相同,用于填充name成员。

(3) kobject_getkobject_put

这两个函数在内核目录/lib/kobject.c中被定义为:

struct kobject *kobject_get(struct kobject *kobj);

void kobject_put(struct kobject * kobj);

这两个函数是对引用计数进行操作的,在kobject中,只要对象的引用计数存在,这个对象(和支持它的代码)就必须继续存在。kobject_get函数对计数加1,同时返回该对象的指针。而kobject_put函数将对象的引用计数减1,如果引用计数降为0,还会调用kobject_release释放该kobject对象。

(4) kobject_add

这个函数在内核目录/lib/kobject.c中被定义为:

int kobject_add(struct kobject * kobj);

该函数将kobj对象加入Linux设备层次。挂接该kobject对象到ksetlist链中,增加父目录各级kobject的引用计数,在其parent指向的父目录下创建文件节点,并启动该类型内核对象的hotplug函数。

(5)kobject_init_and_add

这个函数在内核目录/lib/kobject.c中被定义为:

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,

struct kobject *parent, const char *fmt, ...)

该函数实际上是将kobject_init()kobject_add()合并在一起。

(6) kobject_create

这个函数在内核目录/lib/kobject.c中被定义为:

struct kobject *kobject_create(void)

该函数会使用kzalloc()动态地申请一个kobject结构体,并调用kobject_init()进行初始化。

(7) kobject_create_and_add

这个函数在内核目录/lib/kobject.c中被定义为:

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

该函数实际上是kobject_create()kobject_add()的合并版本。

(8) kobject_del

这个函数在内核目录/lib/kobject.c中被分别定义为:

void kobject_del(struct kobject *kobj);

该函数从Linux设备层次(hierarchy)中删除kobj对象。


8.3 attribute

属性是以文件的形式输出到sysfs的目录当中。在内核中用一个结构体表示:

[cpp] view plain copy print ?
  1. struct attribute {  
  2.     const char      *name;  
  3.     mode_t          mode;  
  4. #ifdef CONFIG_DEBUG_LOCK_ALLOC   
  5.     struct lock_class_key   *key;  
  6.     struct lock_class_key   skey;  
  7. #endif   
  8. };  

attribute这个结构体中name就是属性的名字,在sysfs下为文件的名字;mode代表这个文件的读写属性,例如S_IRUGO对应只读属性。通常attribute不单独使用,常内嵌到别的结构体内部使用。例如,在devices子系统,attribute内嵌到结构体device_attribute;而在bus子系统,attribute内嵌到结构体bus_attribute中,等等。在这些结构体中,通常还定义了attribute的读写操作函数showstoresysfs通过函数sysfs_create_file()建立文件,attribute结构体的变量指针就会作为参数传递给这个函数,而在上述子系统通常都会定义自己的创建文件函数,这些函数都会调用sysfs_create_file()


8.3.1 猜测

sysfs中,kobject->kobj_type中内嵌了一个struct attribute **default_attrs,即指针的指针,在C语言中,这种指针的指针通常用于表示一个列表,目录下的所有默认属性文件就存在于这个列表中(当然还有一些非默认的文件是不存在于这个表中的),对于所有属性(包括非缺省)都有一个共同的showstore,即包含于kobj_type中的sysfs_ops结构体,当系统调用过来的时候,首先调用的是sysfs_opsshow(读)或store(写),然后sysfs_opsshowstore又会去寻找属性文件自己showstore,并调用。而属性文件自己的showstore就存在于driver_attributeclass_attribute等等各子系统的相应结构体中。


8.4 kref

kref表示该对象的引用的计数,跟踪对象生命周期的一种方法是使用引用计数。当没有内核代码持有该对象的引用时,该对象将结束自己的有效生命期并可被删除。内核提供两个函数kobject_get()kobject_put()分别用于增加和减少引用计数,当引用计数为0时,所有该对象使用的资源将被释放。


8.5 kobj_type

Kobj_type数据结构在Linux-2.6.38中被定义成:

[cpp] view plain copy print ?
  1. struct kobj_type {  
  2.     void (*release)(struct kobject *kobj);  
  3.     const struct sysfs_ops *sysfs_ops;  
  4.     struct attribute **default_attrs;  
  5.     const struct kobj_ns_type_operations*(*child_ns_type)(struct kobject *kobj);  
  6.     const void *(*namespace)(struct kobject *kobj);  
  7. };  


release函数用于释放kobject占用的资源。sysfs_ops指针指向sysfs操作表或一个sysfs文件系统缺省属性列表。sysfs操作表包括两个函数store()show()。当用户态读取属性时(read系统调用),show()函数被调用,该函数将指定属性值存入buffer中返回给用户态;而store()函数用于存储用户态传入的属性值。


8.6 kset

kset最重要的是建立上层(sub-system)和下层的(kobject)的关联性。kobject也会利用它了分辨自已是属于那一个类型,然後在/sys下建立正确的目录位置。而kset的优先权比较高,下层kobject会利用自已的*kset成员找到自已所属的kset,把*ktype成员指定成kset->kobj->ktype,除非没有定义kset,才会用自身的ktype来建立关系(例如busclass这些建立在sysfs顶层目录的文件夹)。Kobject通过kset组织成层次化的结构,kset是具有相同类型的kobject的集合,在内核中用kset数据结构表示,定义为:

[cpp] view plain copy print ?
  1. /** 
  2.  * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. 
  3.  * 
  4.  * A kset defines a group of kobjects.  They can be individually 
  5.  * different "types" but overall these kobjects all want to be grouped 
  6.  * together and operated on in the same manner.  ksets are used to 
  7.  * define the attribute callbacks and other common events that happen to 
  8.  * a kobject. 
  9.  * 
  10.  * @list: the list of all kobjects for this kset 
  11.  * @list_lock: a lock for iterating over the kobjects 
  12.  * @kobj: the embedded kobject for this kset (recursion, isn't it fun...) 
  13.  * @uevent_ops: the set of uevent operations for this kset.  These are 
  14.  * called whenever a kobject has something happen to it so that the kset 
  15.  * can add new environment variables, or filter out the uevents if so 
  16.  * desired. 
  17.  */  
  18. struct kset {  
  19.     struct list_head list;  
  20.     spinlock_t list_lock;  
  21.     struct kobject kobj;  
  22.     const struct kset_uevent_ops *uevent_ops;  
  23. };  

kset其实也是一个kobject,因此kset也是一个目录,其内嵌的kobject就提供了这样的功能。但kset通常是一个父目录,其中包含很多子目录,每个子目录对应一个kobject。所有这些kobject被组织成一个双向循环链表,list正是该链表的头。kset也需要一个kobj_type结构,该结构被kset中的所有子kobject共享,而该结构由kset下的kobject->ktype提供(这里容易搞混淆,ksetkobjectkset下所有list列表中的kobject的父对象)。kset也需要维护引用计数,以确定自己的生命周期,同样地,这也依赖于自己的kobject

你可能感兴趣的:(sysfs原理)