快捷搜索:  as  test  1111  test aNd 8=8  test++aNd+8=8  as++aNd+8=8  as aNd 8=8

新蒲京澳门赌场网站:解析Linux中的VFS文件系统机制



本文阐述 Linux 中的文件系统部分,源代码来自基于 IA32 的 2.4.20 内核。总体上说 Linux 下的文件系统主要可分为三大年夜块:一是上层的文件系统的系统调用,二是虚拟文件系统 VFS(Virtual Filesystem Switch),三是挂载到 VFS 中的各实际文件系统,例如 ext2,jffs 等。本文偏重于经由过程详细的代码阐发来解释 Linux 内核中 VFS 的内在机制,在这历程中会涉及到上层文件系统调用和下层实际文件系统的若何挂载。文章试图从一个对照高的角度来解释 Linux 下的新蒲京澳门赌场网站 VFS 文件系统机制。

1. 择要

本文阐述 Linux 中的文件系统部分,源代码来自基于 IA32 的 2.4.20 内核。总体上说 Linux 下的文件系统主要可分为三大年夜块:一是上层的文件系统的系统调用,二是虚拟文件系统 VFS(Virtual Filesystem Switch),三是挂载到 VFS 中的各实际文件系统,例如 ext2,jffs 等。本文偏重于经由过程详细的代码阐发来解释 Linux 内核中 VFS 的内在机制,在这历程中会涉及到上层文件系统调用和下层实际文件系统的若何挂载。文章试图从一个对照高的角度来解释 Linux 下的 VFS 文件系统机制,以是在论述中更偏重于全部模块的主脉络,而不拘泥于细节,同时配有多少张插图,以赞助读者理解。

相对来说,VFS 部分的代码对照繁琐繁杂,盼望读者在涉猎完本文之后,能对 Linux 下的 VFS 整体运作机制有个清楚的理解。建议读者在涉猎本文前,先考试测验着自己涉猎一下文件系统的源代码,以便建立起 Linux 下文件系统最基础的观点,比如至少应认识 super block, dentry, inode,vfsmount 等数据布局所表示的意义,这样再来涉猎本文以便加深理解。

2. VFS 概述

VFS 是一种软件机制,大概称它为 Linux 的文件系统治理者更确切点,与它相关的数据布局只存在于物理内存傍边。以是在每次系统初始化时代,Linux 都首先要在内存傍边构造一棵 VFS 的目录树(在 Linux 的源代码里称之为 namespace),实际上就是在内存中建立响应的数据布局。VFS 目录树在 Linux 的文件系统模块中是个很紧张的观点,盼望读者不要将其与实际文件系统目录树肴杂,在笔者看来,VFS 中的各目录其主要用途是用来供给实际文件系统的挂载点,当然在 VFS 中也会涉及到文件级的操作,本文不阐述这种环境。下文提到目录树或目录,假如不分外阐明,均指 VFS 的目录树或目录。图 1 是一种可能的目录树在内存中的影像:

图 1:VFS 目录树布局

3. 文件系统的注册

这里的文件系统是指可能会被挂载到目录树中的各个实际文件系统,所谓实际文件系统,等于指VFS 中的新蒲京澳门赌场网站实际操作终极要经由过程它们来完成而已,并不料味着它们必然要存在于某种特定的新蒲京澳门赌场网站存储设备上。比如在笔者的 Linux 机械下就注册有 "rootfs"、"proc"、"ext2"、"sockfs" 等十几种文件系统。

3.1 数据布局

在 Linux 源代码中,每种实际的文件系统用以下的数据布局表示:

struct file_system_type {

const char *name;

int fs_flags;

struct super_block *(*read_super) (struct super_block *, void *, int);

struct module *owner;

struct file_system_type * next;

struct list_head fs_supers;

};

注册历程实际上将表示各实际文件系统的 struct file_system_type 数据布局的实例化,然后形成一个链表,内核顶用一个名为 file_systems 的全局变量来指向该链表的表头。

3.2 注册 rootfs 文件系统

在浩繁的实际文件系统中,之以是零丁先容 rootfs 文件系统的注册历程,其实是由于该文件系统 VFS 的关系太过亲昵,假如说 ext2/ext3 是 Linux 的本土文新蒲京澳门赌场网站件系统,那么 rootfs 文件系统则是 VFS 存在的根基。一样平常文件系统的注册都是经由过程 module_init 宏以及 do_initcalls() 函数来完成(读者可经由过程涉猎module_init 宏的声明及 archi386vmlinux.lds 文件来理解这一历程),然则 rootfs 的注册却是经由过程 init_rootfs() 这一初始化函数来完成,这意味着 rootfs 的注册历程是 Linux 内核初始化阶段弗成瓜分的一部分。

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

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 布局块下方的数字表示它们在内存里被分配的先后顺序。限于篇幅的缘故原由,各布局中只给出了部分成员变量,读者可以对比源代码根据图中所示按图索骥,以加深理解。

VFS 中各目录的主要用途是为今后挂载文件系统供给挂载点。以是真正的文件操作照样要经由过程挂载后的文件系统供给的功能接口来进行。

5. VFS 下目录的建立

为了更好地舆解 VFS,下面我们用一个实际例子来看看 Linux 是若何在 VFS 的根目录下建立一个新的目录 "/dev" 的。

要在 VFS 中建立一个新的目录,首先我们得对该目录进行搜索,搜索的目的是找到将要建立的目录其父目录的相关信息,由于"皮之不存,毛将焉附"。比如要建立目录 /home/ricard,那么首先必须沿目录路径进行逐层搜索,本例中先从根目录找起,然后在根目录下找到目录 home,然后再往下,就是要新建的目录名 ricard,那么前面讲得要先对目录搜索,在该例中就是要找到 ricard 这个新目录的父目录,也便是 home 目录所对应的信息。

当然,假如搜索的历程中发明差错,比如要建目录的父目录并不存在,或者当提高程并无响应的权限等等,这种环境系统一定会调用相关历程进行处置惩罚,对付此种环境,本文略过不提。

Linux 下用系统调用 sys_mkdir 来在 VFS 目录树中增添新的节点。同时为共同路径搜索,引入了下面一个数据布局:

struct nameidata {

struct dentry *dentry;

struct vfsmount *mnt;

struct qstr last;

unsigned int flags;

int last_type;

};

这个数据布局在路径搜索的历程顶用来记录相关信息,起着类似"路标"的感化。此中前两项中的 dentry记录的是要建目录的父目录的信息,mnt 成员接下来会解释到。后三项记录的是所查找路径的着末一个节点(即待建目录或文件)的信息。 现在为建立目录 "/dev" 而调用 sys_mkdir("/dev", 0700),此中参数 0700 我们不去管它,它只是限制将要建立的目录的某种模式。sys_mkdir 函数首先调用 path_lookup("/dev", LOOKUP_PARENT, &nd);来对路径进行查找,此中 nd 为 struct nameidata nd 声明的变量。在接下来的论述中,由于函数调用关系的繁琐,为了凸起历程主线,将不再严格按照函数的调用关系来进行描述。

这样,当调用 sys_mkdir 成功地在 VFS 的目录树中新建立一个目录 "/dev" 之后,在图 3 的根基上,新的数据布局之间的关系便如图 4 所示。图 4 中颜色较深的两个矩形块 new_inode 和 new_entry 就是在sys_mkdir() 函数中新分配的内存布局,至于图中的 mnt,sb,dentry,inode 等布局,仍为图 3 中响应的数据布局,其互相之间的链接关系不变(图中为避免过多的链接曲线,轻忽了一些链接关系,如 mnt 和 sb,dentry之间的链接,读者可在图 3 的根基上参看图 4)。

必要强调一点的是,既然 rootfs 文件系统被 mount 到了 VFS 树上,那么它在 sys_mkdir 的历程中一定会介入进来,事实上在全部历程中,rootfs 文件系统中的 ramfs_mkdir、ramfs_lookup 等函数都曾被调用过。

图 4: 在 VFS 树中新建一目录 "dev"

6. 在 VFS 树中挂载文件系统

在本节中,将描述在 VFS 的目录树中向此中某个目录(安装点 mount point)上挂载(mount)一个文件系统的历程。

这一历程可简单描述为:将某一设备(dev_name)上某一文件系统(file_system_type)安装到VFS目录树上的某一安装点(dir_name)。它要办理的问题是:将对 VFS 目录树中某一目录的操作转化为详细安装到其上的实际文件系统的对应操作。比如说,假如将 hda2 上的根文件系统(假设文件系统类型为 ext2)安装到了前一节中新建立的 "/dev" 目录上(此时,"/dev" 目录就成为了安装点),那么安装成功之后应达到这样的目的,即:对 VFS 文件系统的 "/dev" 目录履行 "ls" 指令,该条指令应能列出 hda2 上 ext2 文件系统的根目录下所有的目录和文件。很显然,这里的关键是若何将对 VFS 树中 "/dev" 的目录操作指令转化为安装在其上的 ext2 这一实际文件系统中的响应指令。以是,接下来的论述将捉住若何转化这一核心问题。在论述之前,读者不妨自己设想一下 Linux 系统会若何办理这一问题。记着:对目录或文件的操作将终极由目录或文件所对应的 inode 布局中的 i_op 和 i_fop 所指向的函数表中对应的函数来履行。以是,不管终极办理规划若何,都可以设想一定要经由过程将对 "/dev" 目录所对应的 inode 中 i_op 和 i_fop 的调用转换到 hda2 上根文件系统 ext2 中根目录所对应的 inode 中 i_op 和 i_fop 的操作。

do_kern_mount() 函数要做的工作,就是建立一新的安装区域块,详细的内容在前面的章节 VFS 目录树的建立中已经论述过,这里不再赘述。

graft_tree() 函数要做的工作就是将 do_kern_mount() 函数返回的一 struct vfsmount 类型的变量加入到安装系统链表中,同时 graft_tree() 还要将新分配的 struct vfsmount 类型的变量加入到一个hash表中,其目的我们将会在今后看到。

这样,当 do_kern_mount() 函数返回时,在图 4 的根基上,新的数据布局间的关系将如图 5 所示。此中,红圈区域里面的数据布局就是被称做安装区域块的器械,此中不妨称 e2_mnt 为安装区域块的指针,蓝色箭头曲线即构成了所谓的安装系统链表。

在把这些函数调用后形成的数据布局关系理清楚之后,让我们回到本章节开始提到的问题,即将 ext2 文件系统安装到了 "/dev " 上之后,对该目录上的操作若何转化为对 ext2 文件系统响应的操作。从图 5上看到,对 sys_mount() 函数的调用并没有直接改变 "/dev " 目录所对应的 inode (即图中的 new_inode变量)布局中的 i_op 和 i_fop 指针,而且 "/dev " 所对应的 dentry(即图中的 new_dentry 变量)布局仍旧在 VFS 的目录树中,并没有被从此中暗藏起来,响应地,来自 hda2 上的 ext2 新蒲京澳门赌场网站文件系统的根目录所对应的 e2_entry 也不是如当初笔者所想象地那样将 VFS 目录树中的 new_dentry 取而代之,那么这之间的转化到底是若何实现的呢?

请读者留意下面的这段代码:

while (d_mountpoint(dentry) && __follow_down(&nd->mnt, &dentry));

这段代码在 link_path_walk() 函数中被调用,而 link_path_walk() 终极又会被 path_lookup() 函数调用,假如读者涉猎过 Linux 关于文件系统部分的代码,应该知道 path_lookup() 函数在全部 Linux 繁琐的文件系统代码中属于一个紧张的根基性的函数。简单说来,这个函数用于解析文件路径名,这里的文件路径名和我们日常平凡在利用法度榜样中所涉及到的观点相同,比如在 Linux 的利用法度榜样中 open 或 read 一个文件 /home/windfly.cs 时,这里的 /home/windfly.cs 便是文件路径名,path_lookup() 函数的责任便是对文件路径名中进行搜索,直到找到目标文件所属目录所对应的 dentry 或者目标直接便是一个目录,笔者不想在有限的篇幅里具体解释这个函数,读者只要记着 path_lookup() 会返回一个目标目录即可。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

您可能还会对下面的文章感兴趣: