未安装文件系统的磁盘称之为生磁盘,生磁盘也可以作为文件读写,linux中一切皆文件。
磁盘分区生磁盘可以被分区,分区中可以安装文件系统,常见的文件系统有fat32、ext2、ext4等。分区后的磁盘结构布局如下图,其中主引导扇区记录了分区信息,并且包含引导代码可用于引导操作系统。
文件系统分区内可以安装指定文件系统,同一磁盘多个分区文件系统不要求相同。MINIX文件系统布局如下:
- 引导块:若作为引导分区,将存放操作系统的引导程序代码,否则空置。
- 超级块:用于存放磁盘设备上文件系统结构的信息,说明各部分的大小。
- i节点位图:标记i节点数据元素是否被使用
- 逻辑块位图:标记磁盘数据块是否被使用
- i节点区:用于存放inode节点数据,一个文件对应一个inode节点,inode节点存储文件属性数据。
- 数据区:以固定大小盘块(1k)为单位进行动态分配和回收,用于存储数据,类似内存分页。
用于存放磁盘设备上文件系统结构的信息,说明各部分的大小。
图中列出的数据包含两部分:出现在盘上和内存中的字段和仅在内存中使用的字段,仅在内存中使用的字段是内核读取超级块后所附加的一些用于管理使用的字段信息。
inode节点inode节点保存对应文件的属性信息,其中i_zone数组使用类似多级页表的方式维护文件数据块,即记录文件逻辑块到物理块之间的映射关系。
i_zone数组i_zone数组包含直接盘块号、一次间接盘块号和二次间接盘块号。一次盘块号可视为单级页表,一次间接盘块号可视为二级页表、二次间接盘块号可视为三级页表。
这种处理方式的好处在于,对于小文件,通过直接块号可快速定位数据块;对于中等类型的文件,一次间接块可以维护较多数据块的同时,具有较快的访问速度;对于大型文件,二次间接盘块号可以维护大量磁盘块,但访问速度较慢。
内存多级页表与i_zone直接区别:不同进程具有固定大小的虚地址空间,并且对其整个虚地址空间的内存,都有可能访问到,因此使用多级页表。文件系统内存在很多大小不一的文件,综合考虑对不同大小文件的特点,使用1-3级磁盘块表可以分别处理小、中、大文件。
目录结构- 树形目录结构
文件系统目录结构为树形,根节点为根目录,目录可以指向若干个子目录或子文件。
- 文件查找过程
通过文件inode节点,可以定位文件数据块,那如何通过文件路径定位到具体文件?
文件系统主要包含文件和目录两种文件,目录是一种特殊的文件,其文件内容存储其目录下文件名->inode节点号的映射信息。文件查找开始于根目录,根目录号固定为0,不需要查找即可直接打开。
举例说明文件查找过程,给定存在路径/name1/name2/name3
查找具体文件过程:
1)通过根节点inode号,打开根目录,读取其文件内容,即目录下文件名->inode节点号映射表,找到name1目录inode节点号n1
2)通过name1的inode号n1,打开name1目录,读取其文件内容,即目录下文件名->inode节点号映射表,找到name1目录inode节点号n2
3)通过name2的inode号n2,打开name2目录,读取其文件内容,即目录下文件名->inode节点号映射表,找到name3目录inode节点号n3
4)通过name3的inode号n3,打开name3文件
- 文件数据块定位过程
1)打开文件:先通过文件查找找到文件inode节点号,然后打开文件,即读取inode至内存。
2)定位数据块:通过文件inode节点,访问其i_zone数组,进一步可以定位具体的数据所在磁盘块号。
文件链接- 硬链接
硬链接,在指定目录文件中,生成一个文件,即建立文件名->inode节点号的映射。不同之处在于,硬链接文件的inode号是其它文件的inode号,即多个文件共享一个inode。多个文件共享inode节点时,会引起inode节点引用计数增加。
硬链接示意图如下。
- 软链接(符号链接)
软链接,在指定目录文件中,生成一个文件,建立文件名->文件路径的映射。软链接文件项不直接指向被指向文件的inode号,而是记录其文件路径。符号链接不直接共享inode,不会引起inode节点引用计数增加。
打开软链接文件时,先获取其指向的文件路径,再通过指向的文件路径打开文件。
软链接文件示意图如下,其中蓝色线表示软链接。
- 删除被链接文件
硬链接:多个文件指向同一个inode节点,引用计数等于文件数量,删除其中一个文件,只会导致引用计数减小1,引用计数为零时,文件才会被删除。
软链接:删除软链接,不会对被指向文件产生任何影响;删除被指向文件,软链接可能会失效。
高速缓存高速缓存是内核访问磁盘文件系统数据的必经之道,高速缓存用于解决CPU速度与磁盘IO速度不匹配的问题。因为CPU速度与磁盘IO速度差距较大,CPU同步读写磁盘数据,会导致CPU性能的浪费。
内核维护一个高速缓冲区池,按块使用和管理,可用于缓存磁盘数据,提高访问磁盘数据的性能。
buffer.c文件包含实现高速缓冲区的程序。内核代码通过指定设备号和逻辑块号来调用块读写函数。这些接口函数有,bread()、breada()、bread_page()。
- bread:块读取函数
- breada:块提前预读函数
- bread_page:页块读取函数,一个内存页通常为4k大小、磁盘块通常为1k大小
该部分函数用于处理文件系统的元数据,包括超级块、位图、inode节点等。该部分函数被内核其它部分代码调用,不是提供给用户代码调用的系统调用接口。
文件系统底层处理函数包含在以下5个文件中:
-
bitmap.c:包含对i节点位图和逻辑块位图进行释放和占用处理函数,free_inode()、new_inode()、free_block()、new_block()
-
truncate.c:对数据文件长度截断为0的函数truncate(),可释放文件所有数据块。
-
inode.c:包含分配释放inode节点函数iget()和iput(),以及根据inode节点,获取文件逻辑块号函数bmap()。
-
namei.c:包含函数namei(),获取指定路径的inode节点
-
super.c:包的程序专门用于处理系统超级块,包括函数get_super()、free_super()等。还包括几个文件系统加载/卸载处理函数和系统调用,如sys_mount()等。
下图展示了文件系统不同部分对应的底层处理函数:
文件中数据的访问操作该部分函数用于处理文件的读、写操作。
- 文件数据访问函数
用户程序调用read()、write()函数读写文件,read()、write()函数根据文件类型与其所在设备,进一步调用设备对应的读写函数如read_pipe()、write_pipe()等。
- 打开文件使用的内核数据结构
内核使用文件结构file、文件表file_table[]和内存中的i节点表inode_table[]来管理对文件的操作访问。
- 文件指针数组。进程使用一个文件指针数组管理打开的文件,其中的文件指针(下标号)指向文件表中对应的文件结构file。每个进程有自己的文件指针数组。
- 文件表:表项为file数据,用于在文件句柄于内存i节点项之间建立关系,其包含文件类型、访问属性、读写指针、文件句柄引用计数等信息。因为不同进程可能使用不同的访问模式打开同一文件,并且需要单独的读写指针等数据。所有进程共享文件表(file_table)。
- 内存i节点表:表项存储i节点信息,存储所有进程打开文件的i节点信息。所有进程共享内存i节点表。
思考:文件描述符指向file结构、file结构指向内存inode节点,允许存在多对一的情况。什么场景会存在多个文件描述符指向同一个file结构;什么场景存在多个file结构指向同一个内存inode节点。
open同一个文件:多个file数据指向同一inode节点
dul同一文件描述符:多个文件描述符指向同一文件表项—file数据。
fork进程:仅复制文件描述符表
内核数据结构
file数据结构
文件和目录管理系统调用- open.c:实现文件管理相关的系统调用。主要包括创建、打开、关闭和属性修改。
- exec.c:实现对二进制可执行文件和shell脚本文件的加载与执行,其中主要的函数为do_execve(),是exec()函数簇的主要实现函数。
- fcntl.c:实现文件系统控制调用fcntl()和文件描述符的复制系统调用dup()、dup2()。dup主要用于多个文件描述符指向同一file结构的场景。
- ioctl.c:实现输入/输出控制系统调用ioctl。主要调用tty_ioctl()函数,对终端io进行控制。
- stat.c:实现取文件状态信息系统调用stat()和fstat。
- MINIX文件系统空闲inode节点项和空闲磁盘块均通过位图管理,位图记录对应项是否空闲。
- 硬链接和软链接的区别在于,链接是直接指向inde节点,还是通过指向其他文件路径间接指向inode节点。
- linux内核具有io高速缓冲区,用于缓存磁盘数据,提高磁盘数据的访问效率。
- 打开文件内核数据结构具有三级映射关系:文件描述符->file数据->内存inode节点。open同一文件,多个file指向同一内存inode;dup文件描述符,多个文件描述符指向同一file。