3sysfs文件系统sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。该文件系统的目录层次结构严格按照内核的数据结构组织。除了二进制文件外(只有特殊场合才使用),sysfs文件内容均以ASCII格式保存,且一个文件只保存一个数据,另外,一个文件不可大于一个内存页(通常为4096字节)。sysfs提供一种机制,使得可以显式的描述内核对象、对象属性及对象间关系。sysfs有两组接口,一组针对内核,用于将设备映射到文件系统中,另一组针对用户程序,用于读取或操作这些设备。表2描述了内核中的sysfs要素及其在用户空间的表现:sysfs在内核中的组成要素在用户空间的显示内核对象(kobject)目录对象属性(attribute)文件对象关系(relationship)链接(SymbolicLink)表2:sysfs内部结构与外部表现在Ubuntu或Fedora等Linux系统中,我们可以用ls–F路径命令来通过文件后缀查看文件类型。“/”表示文件夹,“@”表示链接,没有后缀就是文件了。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/module_name/,并且在这个目录下会出现一些属性文件和属性目录来表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等;编译为内联方式的模块则只在当它有非0属性的模块参数时会出现对应的/sys/module/module_name,这些模块的可用参数会出现在/sys/modules/modname/parameters/param_name中,o如/sys/module/printk/parameters/time这个可读写参数控制着内联模块printk在打印内核消息时是否加上时间前缀;o所有内联模块的参数也可以由module_name.param_name=value的形式写在内核启动参数上,如启动内核时加上参数printk.time=1与向/sys/module/printk/parameters/time写入1的效果相同;没有非0属性参数的内联模块不会出现于此。/sys/power这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。表3:sysfs目录结构深入理解sysfssysfs_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操作函数。文件操作比较常见:open、read、write等。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,对应sysfs为sysfs_lookup函数*/conststructfile_operations*i_fop;/*一组文件操作函数,对于sysfs为sysfs的open/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_dirent、inode、dentry三者关系:图3-1:sysfs在内存中的形态如上图sysfs超级块sysfs_sb、dentry根目录root、sysfs_direct根目录sysfs_root都是在sysfs初始化时创建。sysfs_root下的子节点是添加设备对象或对象属性时调用sysfs_create_dir/sysfs_create_file创建的,同时会申请对应的inode的索引号s_ino。注意此时并未创建inode。inode是在用到的时候调用sysfs_get_inode函数创建并依据sysfs_sb地址和申请到的s_ino索引计算散列表位置放入其中。dentry的子节点也是需要用的时候才会创建。比如open文件时,会调用path_walk根据路径一层层的查找指定dentry,如果找不到,则创建一个,并调用父dentry的inode的lookup函数(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获取对应的inode,sysfs_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,i