linux sysfs(7)

来源:互联网 发布:php restful 框架 编辑:程序博客网 时间:2024/05/28 11:48

关于linux的文件系统,我觉得需要补充说明一下,中间牵涉的概念比较多,文件系统,文件,目录,目录文件,超级块,挂载,节点,挂载点等等一对概念糅合在一起有点晕。

文件系统,file system是指由标题、目录、节点和数据实际存储地方组成的一个系统,我们可以根据这个系统提供的方法存储文件和提取文件。

所以,从数据结构上,文件系统至少五个方面表示,一个是标题、一个超级块、一个是目录,一个是节点,一个是数据。

所以,如果挂载跟系统,也需要这个五个东西,这五个东西分别是:

rootfs (struct file_system_type)

sb (struct super_block)

dentry (struct dentry)

inode ()

嗯?怎么回事,怎么只找到了四个东西,还有另外一个东西,就是mnt,看起来不像是数据啊?

其实就是数字,老子说过,授人以鱼,不如授之以渔,这里mnt其实就是给出如何得到这些数据的方法,结合目录和节点表,再有方法,当然一下子就得到数据了。也就是如果你的数组在硬盘,那么mnt就给你硬盘数据的取值方法,如果你的数据在内存,它就给你内存取数据的方法。

书接前文:

你现在必须找办公室,而且找到合适的了,那么下一步,就是按照你的要求来不止家具了和警卫了。怎么布置?如下几步:

1)申请一个rootfsfile_system_type并且做初始化,然后把file_systems指针知道这里

2)申请一个超级块大小的内存,然后初始化,然后把这个申请的超级块指针填写到上面file_system_typefs_supers

3)申请一块内存,用来作为dentry的入口

4)申请一个inode 内存空间,并初始化其部分成员变量,然后把该申请的内存地址写到

5)申请一个mnt的内存空间,并初始化其部分成员变量,

------------------------------下面摘抄一下一位同学对上述过程的表述-----------------------------------

既然是树,所以根是其赖以存在的基础,本节阐述 Linux 在初始化阶段是如何建立根结点的,即 "/"目录。这其中会包括挂载 rootfs 文件系统到根目录 "/" 的具体过程。构造根目录的代码是在 init_mount_tree() 函数 (fs\namespace.c) 中。

首先,init_mount_tree() 函数会调用 do_kern_mount("rootfs", 0, "rootfs", NULL) 来挂载前面已经注册了的 rootfs 文件系统。这看起来似乎有点奇怪,因为根据前面的说法,似乎是应该先有挂载目录,然后再在其上挂载相应的文件系统,然而此时 VFS 似乎并没有建立其根目录。没关系,这是因为这里我们调用的是 do_kern_mount(),这个函数内部自然会创建我们最关心也是最关键的根目录(在 Linux 中,目录对应的数据结构是 struct dentry)。

在这个场景里,do_kern_mount() 做的工作主要是:

1)调用 alloc_vfsmnt() 函数在内存里申请了一块该类型的内存空间(struct vfsmount *mnt),并初始化其部分成员变量。

2) 调用 get_sb_nodev() 函数在内存中分配一个超级块结构 (struct super_block) sb,并初始化其部分成员变量,将成员 s_instances 插入到 rootfs 文件系统类型结构中的 fs_supers 指向的双向链表中。

3) 通过 rootfs 文件系统中的 read_super 函数指针调用 ramfs_read_super() 函数。还记得当初注册rootfs 文件系统时,其成员 read_super 指针指向了 ramfs_read_super() 函数,参见图2.

4) ramfs_read_super() 函数调用 ramfs_get_inode() 在内存中分配了一个 inode 结构 (struct inode) inode,并初始化其部分成员变量,其中比较重要的有 i_op、i_fop 和 i_sb:

inode->i_op = &ramfs_dir_inode_operations;inode->i_fop = &dcache_dir_ops;inode->i_sb = sb;

这使得将来通过文件系统调用对 VFS 发起的文件操作等指令将被 rootfs 文件系统中相应的函数接口所接管。

图3

图3

5) ramfs_read_super() 函数在分配和初始化了 inode 结构之后,会调用 d_alloc_root() 函数来为 VFS的目录树建立起关键的根目录 (struct dentry)dentry,并将 dentry 中的 d_sb 指针指向 sb,d_inode 指针指向 inode。

6) 将 mnt 中的 mnt_sb 指针指向 sb,mnt_root 和 mnt_mountpoint 指针指向 dentry,而 mnt_parent指针则指向自身。

这样,当 do_kern_mount() 函数返回时,以上分配出来的各数据结构和 rootfs 文件系统的关系将如上图 3 所示。图中 mnt、sb、inode、dentry 结构块下方的数字表示它们在内存里被分配的先后顺序。限于篇幅的原因,各结构中只给出了部分成员变量,读者可以对照源代码根据图中所示按图索骥,以加深理解。

最后,init_mount_tree() 函数会为系统最开始的进程(即 init_task 进程)准备它的进程数据块中的namespace 域,主要目的是将 do_kern_mount() 函数中建立的 mnt 和 dentry 信息记录在了 init_task 进程的进程数据块中,这样所有以后从 init_task 进程 fork 出来的进程也都先天地继承了这一信息,在后面用sys_mkdir 在 VFS 中创建一个目录的过程中,我们可以看到这里为什么要这样做。为进程建立 namespace 的主要代码如下:

namespace = kmalloc(sizeof(*namespace), GFP_KERNEL);   list_add(&mnt->mnt_list, &namespace->list);  //mnt is returned by do_kern_mount()namespace->root = mnt;init_task.namespace = namespace;for_each_task(p) {get_namespace(namespace);p->namespace = namespace;}set_fs_pwd(current->fs, namespace->root, namespace->root->mnt_root);set_fs_root(current->fs, namespace->root, namespace->root->mnt_root);

该段代码的最后两行便是将 do_kern_mount() 函数中建立的 mnt 和 dentry 信息记录在了当前进程的 fs结构中。

以上讲了一大堆数据结构的来历,其实最终目的不过是要在内存中建立一颗 VFS 目录树而已,更确切地说, init_mount_tree() 这个函数为 VFS 建立了根目录 "/",而一旦有了根,那么这棵数就可以发展壮大,比如可以通过系统调用 sys_mkdir 在这棵树上建立新的叶子节点等,所以系统设计者又将 rootfs 文件系统挂载到了这棵树的根目录上。关于 rootfs 这个文件系统,读者如果看一下前面图 2 中它的file_system_type 结构,会发现它的一个成员函数指针 read_super 指向的是 ramfs_read_super,单从这个函数名称中的 ramfs,读者大概能猜测出这个文件所涉及的文件操作都是针对内存中的数据对象,事实上也的确如此。从另一个角度而言,因为 VFS 本身就是内存中的一个数据对象,所以在其上的操作仅限于内存,那也是非常合乎逻辑的事。在接下来的章节中,我们会用一个具体的例子来讨论如何利用 rootfs所提供的函树为 VFS 增加一个新的目录节点。

VFS 中各目录的主要用途是为以后挂载文件系统提供挂载点。所以真正的文件操作还是要通过挂载后的文件系统提供的功能接口来进行。



原创粉丝点击