在 linux 中常见的文件系统 有很多, 如下?
基于磁盘的文件系统, ext2, ext3, ext4, xfs, btrfs, jfs, ntfs?
内存文件系统, procfs, sysfs, tmpfs, squashfs, debugfs?
闪存文件系统, ubifs, jffs2, yaffs ?
文件系统这一套体系在 linux 有一层 vfs 抽象, 用户程序不用关心 底层文件系统的具体实现, 用户只用操作 open/read/write/ioctl/close 的相关 系统调用, 这一层系统调用 会操作 vfs 来处理响应的业务?
vfs 会有上面各种文件系统对应的 读写 相关服务, 进而 将操作下沉到 具体的文件系统?
我们这里 来看一下 sysfs 文件系统, 这是一个 基于 内核内存的文件系统, 读写的都是 kobject 的相关信息项, 由内核代码来组织的树形结构?
?
我们这里的操作是?“cat /sys/module/i8042/parameters/debug”, 访问之前?对应的 inode 还不存在?
sysfs 的?inode 也是懒加载创建的,?当你访问它的时候,才会创建对应的 inode, 这时候?才会在 vfs?中有对应的角色?
sysfs 对应的?iop->lookup 为?kernfs_iop_lookup, 实现如下,?在父级 kernfs_node 下面查询当前文件的?kernfs_node, 然后根据?当前?kernfs_node 去创建,?初始化当前文件对应的?inode
另外就是 kernfs_node 本身会有一套树形结构,?来维护?sysfs?下面各个文件的关系,?以便于这里的?lookup inode 的实现?
kernfs_get_inode 中新建并初始化?inode, ino 取自?kernfs_node 中早就暂存了一个?i_no
iget_locked 中从?super_block 中分配?inode, 并初始化?ino,?将当前?inode?放到了?inode_hashtable 中, 下一次根据?获取直接拿到的是?已有的 inode
kernfs_init_inode 是初始化 inode 的各个函数?
kenfs_init_node 中初始化了?inode->private/i_ops/i_fops 等等,?f_ops 用于后面读写?当前文件的操作?
从上面可以看到,?系统首先维护的是?kernfs_node 的树形结构
然后?真正的文件的获取是在访问给定的文件的时候创建的,?比如?cat “/sys/module/i8042/parameters/debug”, “ls /sys/module/i8042/”
上面的例子是一个 “cat /sys/module/i8042/parameters/debug” 访问到具体的文件的例子, 这里我们来测试一个?访问目录,?然后?懒加载创建目录下面的所有的 文件的 inode 的过程?
在新建?inode 的地方打上断点,?可以看到的是?外层在迭代 “/sys/devices/breakpoint” 目录下面的所有文件[基于?kernfs_node], 然后通过?lstat 来访问给定的文件,?进而实现主动触发了?文件夹下面所有的文件的访问
然后具体的根据?kernfs_node 迭代文件夹下面所有目录的地方是在这里,?dir_emit 会访问给定的文件,?访问上面的?lstat?函数?
接下来问题?便是 kernfs_node 的这棵树的初始化的流程了,?内核是先构造了这棵树,?然后用户/内核 访问 的时候,?再根据?kernfs_node 创建了对应的?inode
kernfs_node树?的初始化是在 内核初始化的阶段,?创建了相关的?kobject, 就会注册?kernfs_node, 挂在?kernfs_node 树上面?
如下是注册?sysfs_root, 也就是?“/sys”
如下是注册 “/sys” 下面的?“fs”, 创建?kobject 的时候,?传入?parent?为?NULL, 默认?parent 是?sysfs_root
又比如我们这里的 i8042驱动?下面的 debug 参数?
此参数是属于?i8042驱动?下面的 参数组 下面的 debug?参数
因此这里在?i8042节点 下面创建了?parameters 节点,?然后作为?debug 参数节点的父节点
然后是循环?i8042 的参数列表,?注册对应的参数在?i8042节点?下面的 parameters节点?上面
创建?kernfs_node 的时候, 传入的?ops 为?sysfs_file_kfops_rw
kn->attr->ops 为?sysfs_file_kfops_rw, kn->priv 为当前 attr?
大多数的?sysfs 的?”文件” 是不单独占用存储空间的?
是通过相应的读写函数?去操作对应的 kobject
读取的链路如下?
file->f_ops 为?inode 的?f_ops, 为kernfs_file_ops[初始化是在?kernfs_init_inode], 其中?read?为?kernfs_fop_read
下一层?seq_read 的?m->op 来自于封装?file?对象的时候,?初始化的?seq_file, 默认的?ops 为?kernfs_seq_ops
再下一层 of->kn 为?kernfs_node, kernfs_node->attr.ops 为上面构造?kernfs_node 的时候传入的?ops?为?sysfs_file_kfops_rw, 其中?seq_show 为?sysfs_kf_seq_show
接下来就是根据 kernfs_node 中存放的?attribute 的信息,?定位到?module_attribute, param_attribute, kernel_attribute, 然后通过?kernel_attribute 的?ops?来读写?kernel_attribute
在 kernfs_fop_open 的时候,?根据?inode, kernfs_node 以及上下文?构造?file?
kernfs_fop_open 中的?seq_open 中构造了 seq_file, ops 初始化为上下文传入的?kernfs_seq_ops,?构造之后的?file->private_data 为?新建的 seq_file
在外层的 kernfs_fop_open 封装?file, 新建?kernfs_open_file 并初始化, 作为了?((seq_file)file->private_data)->private_data
seq_open 中新建了?seq_file, 作为?file->private_data
kernfs_fop_open 中?kernfs_node 是来自于?file->f_path.dentry->d_fsdata, 那么这个?d_fsdata 是在哪里填充进去的呢??
来自于根据?path 向下遍历的时候,?i_op->lookup 中在?dentry?中封装了?d_fsdata 为?kernfs_node
完?