在这个场景里,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:
|
这使得将来通过文档系统调用对 VFS 发起的文档操作等指令将被 rootfs 文档系统中相应的函数接口所接管。
图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 的主要代码如下:
|
该段代码的最后两行便是将 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 中各目录的主要用途是为以后挂载文档系统提供挂载点。所以真正的文档操作还是要通过挂载后的文档系统提供的功能接口来进行。
5. VFS 下目录的建立
为了更好地理解 VFS,下面我们用一个实际例子来看看 Linux 是如何在 VFS 的根目录下建立一个新的目录 "/dev" 的。
要 在 VFS 中建立一个新的目录,首先我们得对该目录进行搜索,搜索的目的是找到将要建立的目录其父目录的相关信息,因为"皮之不存,毛将焉附"。比如要建立目录 /home/ricard,那么首先必须沿目录路径进行逐层搜索,本例中先从根目录找起,然后在根目录下找到目录 home,然后再往下,便是要新建的目录名 ricard,那么前面讲得要先对目录搜索,在该例中便是要找到 ricard 这个新目录的父目录,也就是 home 目录所对应的信息。
当然,假如搜索的过程中发现错误,比如要建目录的父目录并不存在,或当前进程并无相应的权限等等,这种情况系统必然会调用相关过程进行处理,对于此种情况,本文略过不提。
Linux 下用系统调用 sys_mkdir 来在 VFS 目录树中增加新的节点。同时为配合路径搜索,引入了下面一个数据结构:




