Linux 文件系统(二)---运行过程及结构间的关系
(内核2.4.37)
一、首先。看看磁盘。超级块,inode节点在物理上总体的分布情况:
(图示来自:www.daoluan.net)
对于一个分区,相应一个文件系统,一个文件系统事实上本质上还是磁盘的一部分,所以能够包含多个柱面。对于柱面上的数据,主要看看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、
那么依据上面的解释,一个大的框架图例如以下:
同一时候,在内核中。有一个全局的变量super_blocks用于将全部的suoer_block连接在一起,形成一个双向链表。这样就会发现s_list字段就有意义了!
如图:
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[],所谓“文件描写叙述符”事实上就是这个数组的下标!比如:默认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之后,就能够定位到详细的文件数据在磁盘上的位置了!
对于上面的过程,总体的一张图例如以下:
3、多个进程和多个文件之间的关系:
对于一个进程来说,能够打开多个文件。也能够多个进程打开一个文件,对于文件来说,不变的永远是自己的inode节点,变化的不过和进程直接关系的file结构。能够看一下以下的大图: