Linux 文件系统(二)---运行过程及结构间的关系

时间:2023-12-27 15:55:49

(内核2.4.37)

一、首先。看看磁盘。超级块,inode节点在物理上总体的分布情况:

(图示来自:www.daoluan.net)

Linux 文件系统(二)---运行过程及结构间的关系

对于一个分区,相应一个文件系统,一个文件系统事实上本质上还是磁盘的一部分,所以能够包含多个柱面。对于柱面上的数据,主要看看inode节点位图、block位图,i节点,数据块。inode节点位图是为了记录inode节点的使用情况,之前的违章中已经说过,inode节点在文件系统安装的时候。会初始化全部的inode节点,那么形成的位图表示使用or没使用的大表。对于block块也是一样的,记录数据块使用情况。

对于inode节点来说,每一个文件都会相应一个inode节点。文件夹项也会相应一个inode节点。

对于一个文件来说,仅仅相应一个inode节点,可是一个文件能够有多个数据块。由于一个比較大的文件。一个数据块根本存放不了。

所以inode中记录多个文件数据块的信息。

对于文件夹块来说,主要是为了索引而存在,所以里面的内容主要是inode节点号和文件名称,事实上就是一个映射表形式的东西。

二、

上一篇中对于VFS有一个简单的介绍与理解,我们知道,与用户打交道的是VFS,然后VFS与底层真正的文件系统交流。那我们知道在一个VFS以下,同意存在非常多的“文件系统类型”,比如ext2。ext3。ext4,sysfs,proc等等。

这些类型是能够共存的,同一时候,对于每个类型来说,能够存在多个文件系统实体,比如:在一个文件夹下有多个子文件夹,子文件夹之间的文件系统类型能够不一样。也能够有部分是一样的类型,借用windows中的样例说就是,D盘和E盘能够都是NTFS类型文件系统,也能够是不一样的文件类型系统。

在Linux中。系统有一个全局变量叫做file_systems,这个变量用来管理全部的“文件系统类型”链表。也就是全部的文件系统类型都必须注冊到(链接到)这个链表中,才干够被使用。

假设是自己的文件系统,仅仅要符合VFS的标准,也是能够注冊进去的。终于形成一个单链表形式结构。

而对于一个文件系统类型,使用file_system_type结构表示:

<span style="font-size:14px;">995 struct file_system_type {
996 const char *name;
997 int fs_flags;
998 struct super_block *(*read_super) (struct super_block *, void *, int);
999 struct module *owner;
1000 struct file_system_type * next;
1001 struct list_head fs_supers;
1002 };</span>

字段意思:

name:文件系统类型名称。如ext2。

flags:安装文件类型标志,在fs.h中有定义。

read_super:各种文件系统读入其“超级块”的函数指针,不同的文件系统之间可能不一样。因此读入函数也不一样。

owner:假设这个文件系统是通过一个可安装模块实现的,那么这个指针指向这个模块。

next:这个就是链接到下一个“文件类型”的指针。

fs_supers:属于同样的文件系统类型的全部的super_blocks构成一个双向链表。在超级块中有一个s_instance就是连接这个双链表的连接点。(超级块在上一篇有介绍)

1、

那么依据上面的解释,一个大的框架图例如以下:

Linux 文件系统(二)---运行过程及结构间的关系

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hhbnNoYW5wdA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

同一时候,在内核中。有一个全局的变量super_blocks用于将全部的suoer_block连接在一起,形成一个双向链表。这样就会发现s_list字段就有意义了!

如图:

Linux 文件系统(二)---运行过程及结构间的关系

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvc2hhbnNoYW5wdA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

2、我们在之前也提过,文件系统终于还是要和进程一起协作的。不论什么对于文件的操作都是基于进程的。由操作系统的知识我们知道,对于进程来说,管理进程的叫“进程控制块PCB”,这个在内核中的结构为:task_struct,这个是非常复杂的一个结构体,对于进程来说,能够有自己的操作的文件,那么进程的文件的信息,也是包括在这个结构中:

283 struct task_struct {
... ...
391 /* filesystem information */
392 struct fs_struct *fs;
393 /* open file information */
394 struct files_struct *files;
... ...
}

代码中的两个字段就是涉及进程的文件的字段。

每一个进程在PCB中保存着一份文件描写叙述符表。文件描写叙述符就是这个表的索引(数组的下标),每一个表项都有一个指向已打开文件的指针。

代码中第一个字段fs:代码本进程自身的文件系统的信息。

比如进程本身的根文件夹。挂载点,当前文件夹等信息。

代码中第二个字段files:保存着本进程涉及的全部的文件的信息的指针。

files_struct结构之前已经说过:files_struct

里面有两个重要字段:

172 struct files_struct {
... ...
178 struct file ** fd; /* current fd array */
... ...
183 struct file * fd_array[NR_OPEN_DEFAULT];
184 };

fd就是涉及到的全部的文件的数组指针。普通情况下fd就是fd_array,可是假设打开的文件超过NR_OPEN_DEFAULT,那么就会又一次分配新的数组。然后fd指向新的数组。

对于一个文件数组来说比如:fd[],所谓“文件描写叙述符”事实上就是这个数组的下标!比如:默认0就是标准输入文件描写叙述符,1是标准输出,2是标准错误。

对于用户来说操作的是这个“文件描写叙述符”。可是对于内核来说。“文件描写叙述符”不过为了找到相应的文件而已!然后全部的在内核中的操作,都是使用实际文件的file指针进行的。关于file结构体在上一篇也说了(之前files_struct链接)。延伸一下:我们在写C语言程序的时候会遇到两个函数,open和fopen。

对于前者,返回的就是一个“文件描写叙述符”,即那个文件数组的下标,对于fopen。返回的是一个FILE的指针,这里面事实上除了“文件描写叙述符”之外。还包含IO缓冲这些信息。文件指针FILE*更上层,FILE指针将文件描写叙述符和缓冲区封装在一起。

OK,那么用户进程打开一个文件的详细的过程是什么呢?以下分析总结一下:首先我们知道用户使用open返回一个“文件描写叙述符”(详细怎么获得,之后再说)。然后在进程PCB中,即task_struct文件数组中找到相应“文件描写叙述符”(数组下标)的文件(file)指针。在file结构体中。f_dentry记录了这个文件的完整文件夹项,一般在内存中会有dentry的缓存,通过这个我们能够找到文件的inode。对于一个dentry来说,也是有自己的inode。文件夹名称之类信息。

总之通过dentry。能够找到终于文件的inode。找到inode之后,就能够定位到详细的文件数据在磁盘上的位置了!

对于上面的过程,总体的一张图例如以下:

Linux 文件系统(二)---运行过程及结构间的关系

3、多个进程和多个文件之间的关系:

对于一个进程来说,能够打开多个文件。也能够多个进程打开一个文件,对于文件来说,不变的永远是自己的inode节点,变化的不过和进程直接关系的file结构。能够看一下以下的大图:

Linux 文件系统(二)---运行过程及结构间的关系