sysfs文件系统

发布时间:   来源:文档文库   
字号:
3sysfs文件系统
sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。该文件系统的目录层次结构严格按照内核的数据结构组织。除了二进制文件外(只有特殊场合才使用)sysfs文件内容均以ASCII格式保存,且一个文件只保存一个数据,另外,一个文件不可大于一个内存页(通常为4096字节)。
sysfs提供一种机制,使得可以显式的描述内核对象、对象属性及对象间关系。sysfs有两组接口,一组针对内核,用于将设备映射到文件系统中,另一组针对用户程序,用于读取或操作这些设备。表2描述了内核中的sysfs要素及其在用户空间的表现:
sysfs在内核中的组成要素在用户空间的显示内核对象(kobject对象属性(attribute
目录文件
对象关系(relationship链接(SymbolicLink
2sysfs内部结构与外部表现

UbuntuFedoraLinux系统中,我们可以用lsF<路径>命令来通过文件后缀查看文件类型。“/”表示文件夹,“@”表示链接,没有后缀就是文件了。
sysfs目录结构
/sys下的子所包含的内容目录
/sys/devices这是内核对系统中所有设备的分层次表达模型,也是/sys文件系统管理设备的
最重要的目录结构;
/sys/dev
这个目录下维护一个按字符设备和块设备的主次号码(major:minor链接到真实的设备(/sys/devices的符号链接文件;
/sys/bus
这是内核设备按总线类型分层放置的目录结构,devices中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成Linux统一设备模型的一部分;
/sys/class这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在
/sys/class/input之下,而不论它们是以何种总线连接到系统。它也是构Linux统一设备模型的一部分;
/sys/kernel这里是内核所有可调整参数的位置,目前只有uevent_helper,kexec_loaded,
mm,和新式的slab分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于sysctl(/proc/sys/kernel接口中;
/sys/module这里有系统中所有模块的信息,不论这些模块是以内联(inlined方式编译到内

核映像文件(vmlinuz中还是编译为外部模块(ko文件,都可能会出现在/sys/module中:

编译为外部模块(ko文件在加载后会出现对应的
/sys/module//,并且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、提供的驱动程序等;
编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的/sys/module/,这些模块的可用参数会出现在/sys/modules//parameters/中,
o/sys/module/printk/parameters/time这个可读写参数控制着
内联模块printk在打印内核消息时是否加上时间前缀;
o所有内联模块的参数也可以由
".="的形式写在内核启动参数上,如启动内核时加上参数"printk.time=1"与向"/sys/module/printk/parameters/time"写入1的效果相同;

没有非0属性参数的内联模块不会出现于此。
/sys/power这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的
电源状态,如可以向其中写入控制命令让机器关机、重启等。
3sysfs目录结构

深入理解sysfs
sysfs_dirent是组成sysfs单元的基本数据结构,它是sysfs文件夹或文件在内存中的代表。sysfs_dirent只表示文件类型(文件夹/普通文件/二进制文件/链接文件)及层级关系,其它信息都保存在对应的inode中。我们创建或删除一个sysfs文件或文件夹事实上只是对以sysfs_dirent为节点的树的节点的添加或删除。sysfs_dirent数据结构如下:structsysfs_dirent{
atomic_ts_count;atomic_ts_active;
structsysfs_dirent*s_parent;/*指向父节点*/
structsysfs_dirent*s_sibling;/*指向兄弟节点,兄弟节点是按照inode索引s_ino的大小顺序链接在一起的。*/
constchar*s_name;/*节点名称*/

union{
structsysfs_elem_dirs_dir;/*文件夹,s_dir->kobj指向sysfs对象*/structsysfs_elem_symlinks_symlink;/*链接*/structsysfs_elem_attrs_attr;/*普通文件*/structsysfs_elem_bin_attrs_bin_attr;/*二进制文件*/};
unsignedints_flags;
ino_ts_ino;/*inode索引,创建节点时被动态申请,通过此值和sysfs_dirent地址可以到inode散列表中获取inode结构*/umode_ts_mode;structiattr*s_iattr;};
inode(indexnode中保存了设备的主从设备号、一组文件操作函数和一组inode操作函数。文件操作比较常见:openreadwrite等。inode操作在sysfs文件系统中只针对文件夹实现了两个函数一个是目录下查找inode函数(.lookup=sysfs_lookup,该函数在找不到inode时会创建一个,并用sysfs_init_inode为其赋值;另一个是设置inode属性函数(.setattr=sysfs_setattr,该函数用于修改用户的权限等。inode结构如下:structinode{
structhlist_nodei_hash;/*散列表链节*/structlist_headi_list;structlist_headi_sb_list;
structlist_headi_dentry;/*dentry链节*/unsignedlongi_ino;/*inode索引*/atomic_ti_count;unsignedinti_nlink;uid_ti_uid;gid_ti_gid;
dev_ti_rdev;/*主从设备号*/
conststructinode_operations*i_op;/*一组inode操作函数,可用其中lookup查找目录下inode,对应sysfssysfs_lookup函数*/
conststructfile_operations*i_fop;/*一组文件操作函数,对于sysfssysfsopen/read/write等函数*/structsuper_block*i_sb;

structlist_headi_devices;union{
structpipe_inode_info*i_pipe;structblock_device*i_bdev;structcdev*i_cdev;};};
dentry(directoryentry的中文名称是目录项,Linux文件系统中某个索引节点(inode的链接。这个索引节点可以是文件,也可以是目录。引入dentry的目的是加快文件的访问。dentry数据结构如下:structdentry{
atomic_td_count;/*目录项对象使用的计数器*/unsignedintd_flags;/*目录项标志*/spinlock_td_lock;/*目录项自旋锁*/
intd_mounted;/*对于安装点而言,表示被安装文件系统根项*/structinode*d_inode;/*文件索引节点(inode*//*
*Thenextthreefieldsaretouchedby__d_lookup.Placethemhere*sotheyallfitinacacheline.*/
structhlist_noded_hash;/*lookuphashlist*/structdentry*d_parent;/*parentdirectory*/structqstrd_name;/*文件名*//*
*d_childandd_rcucansharememory*/union{
structlist_headd_child;/*childofparentlist*/structrcu_headd_rcu;}d_u;
void*d_fsdata;/*与文件系统相关的数据,在sysfs中指向sysfs_dirent*/unsignedchard_iname[DNAME_INLINE_LEN_MIN];/*存放短文件名*/

};
sysfs_direntinodedentry三者关系:

3-1sysfs在内存中的形态

如上图sysfs超级块sysfs_sbdentry根目录rootsysfs_direct根目录sysfs_root都是在sysfs初始化时创建。
sysfs_root下的子节点是添加设备对象或对象属性时调用sysfs_create_dir/
sysfs_create_file创建的,同时会申请对应的inode的索引号s_ino注意此时并未创建inodeinode是在用到的时候调用sysfs_get_inode函数创建并依据sysfs_sb地址和申请到的s_ino索引计算散列表位置放入其中。
dentry的子节点也是需要用的时候才会创建。比如open文件时,会调用path_walk根据路径一层层的查找指定dentry如果找不到,则创建一个,并调用父dentryinodelookup函数(sysfs文件系统的为sysfs_lookup)查找对应的子inode填充指定的dentry这里有必要介绍一下sysfs_lookup的实现,以保证我们更加清晰地了解这个过程,函数主体如下:
staticstructdentry*sysfs_lookup(structinode*dir,structdentry*dentry,structnameidata*nd{

structdentry*ret=NULL;
structsysfs_dirent*parent_sd=dentry->d_parent->d_fsdata;//获取父sysfs_directstructsysfs_dirent*sd;structinode*inode;
mutex_lock(&sysfs_mutex;
/*在父sysfs_direct查找名为dentry->d_name.name的节点*/sd=sysfs_find_dirent(parent_sd,dentry->d_name.name;
/*nosuchentry*/if(!sd{
ret=ERR_PTR(-ENOENT;gotoout_unlock;}
/*这儿就是通过sysfs_direct获取对应的inodesysfs_get_inode实现原理上面已经介绍过了*/
/*attachdentryandinode*/inode=sysfs_get_inode(sd;if(!inode{
ret=ERR_PTR(-ENOMEM;gotoout_unlock;}
/*填充目录项,至此一个目录项创建完毕*//*instantiateandhashdentry*/
dentry->d_op=&sysfs_dentry_ops;/*填充目录项的操作方法,该方法只提供一释放inode函数sysfs_d_iput*/
dentry->d_fsdata=sysfs_get(sd;//填充sysfs_directd_instantiate(dentry,inode;//填充inode
d_rehash(dentry;//dentry加入hash
out_unlock:
mutex_unlock(&sysfs_mutex;

returnret;}
sysfs文件open流程
open的主要过程是通过指定的路径找到对应的dentry,并从中获取inode,然后获取一个空的file结构,inode中相关内容赋值给file这其中包括将inodefop赋给filefop因此接下来调用的filp->fop->open其实就是inode里的fop->open新的file结构对应一个文件句柄fd这会作为整个open函数的返回值。之后的read/write操作就靠这个fd找到对应的file结构了。
3-2是从网上找到的,清晰地描述了filedentry以及inode之间的关系:


3-2filedentryinode关系
进程每打开一个文件,就会有一个file结构与之对应。同一个进程可以多次打开同一个文件而得到多个不同的file结构,file结构描述了被打开文件的属性,读写的偏移指针等等当前信息。
两个不同的file结构可以对应同一个dentry结构。进程多次打开同一个文件时,对应的只有一个dentry结构。dentry结构存储目录项和对应文件(inode)的信息。
在存储介质中,每个文件对应唯一的inode结点,但是,每个文件又可以有多个文件名。即可以通过不同的文件名访问同一个文件。这里多个文件名对应一个文件的关系在数据结构中表示就是dentryinode的关系。
inode中不存储文件的名字,它只存储节点号;dentry则保存有名字和与其对应的节点号,所以就可以通过不同的dentry访问同一个inode

sysfs文件read/write流程
sysfs与普通文件系统的最大差异是,sysfs不会申请任何内存空间来保存文件的内容。事实上再不对文件操作时,文件是不存在的。只有用户读或写文件时,sysfs才会申请一页内存(只有一页),用于保存将要读取的文件信息。如果作读操作,sysfs就会调用文件的父对象(文件夹kobject的属性处理函数kobject->ktype->sysfs_ops->show然后通过show函数来调用包含该对象的外层设备(或驱动、总线等)的属性的show函数来获取硬件设备的对应属性值,然后将该值拷贝到用户空间的buff,这样就完成了读操作。写操作也类似,都要进行内核空间ßà用户空间内存的拷贝,以保护内核代码的安全运行。3-1为用户空间程序读sysfs文件的处理流程,其他操作类似:


3-3:用户空间程序读sysfs文件的处理流程

sysfs是用于表现设备驱动模型的文件系统,它基于ramfs。要学习linux的设备驱动模型,就要先做好底层工作,总结sysfs提供给外界的API就是其中之一。sysfs文件系统中提供了四类文件的创建与管理,分别是目录、普通文件、软链接文件、二进制文件。目录层次往

往代表着设备驱动模型的结构,软链接文件则代表着不同部分间的关系。比如某个设备的目录只出现在/sys/devices下,其它地方涉及到它时只好用软链接文件链接过去,保持了设备唯一的实例。而普通文件和二进制文件往往代表了设备的属性,读写这些文件需要调用相应的属性读写。
sysfs是表现设备驱动模型的文件系统,它的目录层次实际反映的是对象的层次。为了配合这种目录,linux专门提供了两个结构作为sysfs的骨架,它们就是structkobjectstructkset。我们知道,sysfs是完全虚拟的,它的每个目录其实都对应着一个kobject,要想知道这个目录下有哪些子目录,就要用到kset。从面向对象的角度来讲,kset继承了kobject的功能,既可以表示sysfs中的一个目录,还可以包含下层目录。对于kobjectkset,会在其它文章中专门分析到,这里简单描述只是为了更好地介绍sysfs提供的APIsysfs模块提供给外界的接口完全展现在include/linux/sysfs.h中。
[cpp]viewplaincopy
1.structattribute{
2.constchar*name;3.structmodule*owner;4.mode_tmode;5.};6.
7.structattribute_group{8.constchar*name;
9.mode_t(*is_visible(structkobject*,10.structattribute*,int;11.structattribute**attrs;12.};
之前说过普通文件是kobject目录的属性展现。structattribute就是属性的通用结构,其它部分在使用时还可以把structattribute内嵌到更大的属性结构中。structattribute_group提供一组属性的集合,这样集中的管理更为方便。
[cpp]viewplaincopy
1.#define__ATTR(_name,_mode,_show,_store{\
2..attr={.name=__stringify(_name,.mode=_mode},\3..show=_show,\4..store=_store,\5.}6.
7.#define__ATTR_RO(_name{\
8..attr={.name=__stringify(_name,.mode=0444},\9..show=_name##_show,\10.}11.

12.#define__ATTR_NULL{.attr={.name=NULL}}13.
14.#defineattr_name(_attr(_attr.attr.name
以上的宏是为了静态初始化属性时更为方便,我们简单将其忽略。
[cpp]viewplaincopy
1.structbin_attribute{2.structattributeattr;3.size_tsize;4.void*private;
5.ssize_t(*read(structkobject*,structbin_attribute*,6.char*,loff_t,size_t;
7.ssize_t(*write(structkobject*,structbin_attribute*,8.char*,loff_t,size_t;
9.int(*mmap(structkobject*,structbin_attribute*attr,10.structvm_area_struct*vma;11.};
structattribute是通用的属性结构,而structbin_attribute就是为二进制属性专门设计的,它在sysfs中表现为二进制文件,大多数是设备配置参数的映射。structbin_attribute恰恰就是把structattribute内嵌到更大结构的样例。
[cpp]viewplaincopy
1.structsysfs_ops{
2.ssize_t(*show(structkobject*,structattribute*,char*;
3.ssize_t(*store(structkobject*,structattribute*,constchar*,size_
t;4.};
structsysfs_ops中包含showstore两个函数指针,它们分别在sysfs文件读和文件写时调用。
[cpp]viewplaincopy
1.intsysfs_schedule_callback(structkobject*kobj,void(*func(void*,2.void*data,structmodule*owner;
sysfs_schedule_callback(会创建一个工作队列,稍后调用func(data。本来sysfs中的属性读写函数是无法删除属性文件或者kobject目录的,因为调用函数时是加锁的,要删除也需要加锁。但这里可以通过工作队列回调的方式实现。
[cpp]viewplaincopy
1.int__must_checksysfs_create_dir(structkobject*kobj;

2.voidsysfs_remove_dir(structkobject*kobj;
3.int__must_checksysfs_rename_dir(structkobject*kobj,constchar*new_name
;
4.int__must_checksysfs_move_dir(structkobject*kobj,5.structkobject*new_parent_kobj;
sysfs_create_dir(创建一个kobject对应的目录,目录名就是kobj->name
sysfs_remove_dir(删除kobj对应的目录。删除一个目录也会相应地删除目录下的文件及子目录。
sysfs_rename_dir(修改kobj对应目录的名称。
sysfs_move_dir(kobj对应的目录移到new_parent_kobj对应的目录下。
[cpp]viewplaincopy
1.int__must_checksysfs_create_file(structkobject*kobj,2.conststructattribute*attr;
3.int__must_checksysfs_chmod_file(structkobject*kobj,structattribute*at
tr,
4.mode_tmode;
5.voidsysfs_remove_file(structkobject*kobj,conststructattribute*attr;

sysfs_create_file(kobj对应的目录下创建attr对应的属性文件。sysfs_chmod_file(修改attr对应的属性文件的读写权限。
sysfs_remove_file(kobj对应的目录下删除attr对应的属性文件。
[cpp]viewplaincopy
1.int__must_checksysfs_create_bin_file(structkobject*kobj,2.structbin_attribute*attr;
3.voidsysfs_remove_bin_file(structkobject*kobj,structbin_attribute*attr
;
sysfs_create_bin_file(kobj目录下创建attr对应的二进制属性文件。sysfs_remove_bin_file(kobj目录下删除attr对应的二进制属性文件。
[cpp]viewplaincopy
1.int__must_checksysfs_create_link(structkobject*kobj,structkobject*tar
get,
2.constchar*name;
3.int__must_checksysfs_create_link_nowarn(structkobject*kobj,4.structkobject*target,

5.constchar*name;
6.voidsysfs_remove_link(structkobject*kobj,constchar*name;
sysfs_create_link(kobj目录下创建指向target目录的软链接,name为软链接文件名称。sysfs_create_link_nowarn(sysfs_create_link(功能相同,只是在软链接文件已存在时不会出现警告。
sysfs_remove_link(删除kobj目录下名为name的软链接文件。
[cpp]viewplaincopy
1.int__must_checksysfs_create_group(structkobject*kobj,2.conststructattribute_group*grp;3.intsysfs_update_group(structkobject*kobj,4.conststructattribute_group*grp;5.voidsysfs_remove_group(structkobject*kobj,6.conststructattribute_group*grp;
7.intsysfs_add_file_to_group(structkobject*kobj,8.conststructattribute*attr,constchar*group;9.voidsysfs_remove_file_from_group(structkobject*kobj,10.conststructattribute*attr,constchar*group;
sysfs_create_group(kobj目录下创建一个属性集合,并显示集合中的属性文件。如果文件已存在,会报错。
sysfs_update_group(kobj目录下创建一个属性集合,并显示集合中的属性文件。文件已存在也不会报错。sysfs_update_group(也用于group改动影响到文件显示时调用。sysfs_remove_group(kobj目录下删除一个属性集合,并删除集合中的属性文件。sysfs_add_file_to_group(将一个属性attr加入kobj目录下已存在的的属性集合groupsysfs_remove_file_from_group(将属性attrkobj目录下的属性集合group中删除。
[cpp]viewplaincopy
1.voidsysfs_notify(structkobject*kobj,constchar*dir,constchar*attr;

2.voidsysfs_notify_dirent(structsysfs_dirent*sd;
sysfs_notify(sysfs_notify_dirent(都是用来唤醒在属性文件上调用select(poll(而阻塞的用户进程。
[cpp]viewplaincopy
1.structsysfs_dirent*sysfs_get_dirent(structsysfs_dirent*parent_sd,2.constunsignedchar*name;3.structsysfs_dirent*sysfs_get(structsysfs_dirent*sd;4.voidsysfs_put(structsysfs_dirent*sd;

sysfs_get(增加目录或文件的引用计数。
sysfs_put(减少目录或文件的引用计数,并在降为零时删除相应的文件或目录,这种删除又会减少上层目录的引用计数。
sysfs_get_dirent(是增加目录parent_sd中名为name的目录或文件的引用计数。虽然同样是引用计数,同样在降为零时有删除动作,但却并非使用kref这种操作更多地继承了文件系统管理时的传统。
[cpp]viewplaincopy
1.voidsysfs_printk_last_file(void;2.int__must_checksysfs_init(void;
sysfs_printk_last_file(是在sysfs崩溃时打印最后一个访问到的文件路径。sysfs_init(是在sysfs模块初始化时调用的。这两个函数都与我们没有什么关系。

本文来源:https://www.2haoxitong.net/k/doc/7400d87c76c66137ef061963.html

《sysfs文件系统.doc》
将本文的Word文档下载到电脑,方便收藏和打印
推荐度:
点击下载文档

文档为doc格式