sysfs文件系统,在Linux kernel中其主要作用是用来实现设备驱动模型中各组件(bus, device and driver)的层次关系,更精确一点是通过这些组件所内含的kobject完成。所以其大部分功能不对普通用户开放,只供内核使用,当然有一小部分功能开放给了用户空间,可以实现文件读写(目录与link则用来展现层次模型)
这个建议看看sysfs_init_inode()函数,对文件系统比较了解的应该知道inode中i_op及i_fop的作用,比如其中对于SYSFS_DIR,其sysfs_dir_inode_operations只定义了struct inode_operations中少量的成员,其他很重要的比如mkdir, rmdir,mknod等都是没有定义的。这其实暗示着目录层面的操作不应该由普通用户来完成 (device/driver/bus加入到系统 ---> 其内含的kobject加入系统 ---> kobject用sysfs以dir和link形式展示其存在与拓扑层次关系,这是Linux设备驱动模型很重要的一个方面)
>>sysfs文件自身的创建
所以在创建其'/'目录时,必须创建出一个文件系统必备的{filesys_type, mount, super block, inode, dentry}这一五元组。按照时间顺序,依次为:
- register_file_system (/proc/filesystems下记录的就是这里的信息,所以写一个简单的文件系统并不复杂,有时间可以自己尝试去写一个。我要是教计算机的老师,就布置这样一道作业,可以很简单,但最好有亮点 ;-))
- mount (allocate mnt -- 一个文件系统只有一个)
- super block (allocate sb -- 一个文件系统只有一个)
- inode -- sysfs_fill_super() --> sysfs_get_inode() (allocate inode and entry for the root dir)
>>sysfs文件的mount
如果不mount,那么就无法将sysfs向用户空间展示出来.按照系统中的时间顺序:
创建rootfs(VFS) ---> mount一个实际的Linux文件系统(比如ext4)到rootfs的"/root"下面--->mount sysfs到ext4的"/sys"下面。因为内核在完成初始化转入到user space之前,会将系统中第一个进程的工作目录设定为rootfs文件系统的"/root",而这个目录其实是被做过手脚(就是被mount了另一个文件系统ext4/btrfs),所以在用户空间(比如shell下)只能看到ext4等文件系统,而sysfs又被mount到了ext4之下。
>>从用户空间发起(文件的读写):
system call --> rootfs vfs -->physical fs -->sysfs inode (sysino) --> sysino ->i_fop-> sysfs_open_file()
inode中DIR部分的sysfs_lookup()将会在user space -->system call --> rootfs vfs -->ext4-->这条调用链中被调用到,用来对一指定的路径进行查找(lookup_open和atomic_open函数中)。
do_sys_open() -->do_filp_open() --> path_openat() --> do_last --> lookup_open()... -->do_dentry_open():
f->f_op = fops_get(inode->i_fop);
if (open) {
error = open(inode, f);
if (error)
goto cleanup_all;
}
所以sysfs inode中的open成员函数指针被调用,也就是sysfs_open_file()被调用到,这样就可以取得当前文件节点(代表一个kobject)的sysfs_ops *ops,后者定义了读取一个kobj的属性文件。
从sysfs_open_file到对应该sysfs文件的kobj属性文件的操作集,是通过一个类型为struct sysfs_buffer的一个对象来实现的:
sysfs_open_file() --> Allocate buffer: buffer->ops = ops; file->private_data = buffer.
这样后续的sysfs_read_file & sysfs_write_file就可以通过file->private_data操作到该kobj上所定义的属性操作集{*show, *store, *namespace} ---- 也即上面的buffer->ops
针对sysfs read的特定操作来自于kobj_type实例中定义的sysfs_ops,后者是sysfs的用户(主要是内核和设备驱动developer)自己定义的操作,用来完成实际文件读写工作。所以很显然,需要在sysfs_open_file()中找到sysfs自己定义的文件操作函数,也就是sysfs_ops的指针的具体实例化后的数值。这个转化的过程发生在sysfs_open_file中,其大致原理是,对于当前正在打开的sysfs文件系统中的一个文件,它应该对应一个kobject,我们假设这个对应的kobject的实例是kobj, 那么sysfs_ops结构体的对象指针就是kobj->ktype->sysfs_ops。所以这意味着若将一个kobject加入系统,如果要求能对其进行sysfs层面的操作,该kobject对象必须具有一个有效的ktype,此处所谓有效的ktype是指不但要实例化出ktype对象,而且要为ktype对象中的sysfs_ops成员赋值
>>而在内核空间在sysfs文件系统中产生一个目录并不会用到inode中这个lookup(). 参见sysfs_create_dir()
创建一个sysfs目录
=====================
对于sysfs文件系统而言,一个目录由一个类型为struct sysfs_dirent的对象来描述,假设该对象名为sd,sd将在sysfs_new_dirent()中通过kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL)来分配。sd->s_name就是要创建的文件或目录名。sd最后将加入sysfs的文件系统中,以红黑树的方式,这意味着sysfs所在的文件树实际上每个目录都是一颗rb树,因此在sysfs文件系统中查找一个目录节点实际是对一颗rb树进行查找(参见sysfs_lookup代码)。