下一个模块将是fs(filesystem)文件系统模块,在开始阅读源码之前,先对Linux0.11中使用的Minix1.0文件系统有个大致的概念,这对以后的代码阅读会有很大的帮助
文件系统是操作系统的重要组成部份,不论是用户文件还是操作系统形成的临时文件等就会通过文件系统来储存,在使用文件时,又要将文件内容从文件系统中读取到显存。因而文件系统须要使用高速设备来储存程序及数据,os一般使用能储存大量信息的块设备来作为文件系统的设备
基本概念
minix文件系统由6个部份组成:引导块、超级块、i节点位图块、逻辑块位图块、i节点块、数据区。对于一个普通的c盘块设备来说,这6个部份的分布如下(以360KB的c盘为例):
图中一个条纹表示一个盘块,每位盘块大小1KB,故一共有360个盘块,现对这6个部份分开描述
引导块
引导块是计算机加电启动时由ROMBIOS手动读入的储存执行代码和数据的盘块。但一个系统中并非所有盘设备都用于作为引导设备,对于不用引导的硬碟,这一块可以不含代码,但必须留出这一块的空间以保证minix文件系统格式的统一。假如内核映像文件置于文件系统中linux系统镜像下载,这么就可以在引导块显存放引导程序,由它来获取、加载文件系统中的内核映像文件
超级块
超级块用于储存文件系统的结构信息,这种信息都有:
其中有个新概念叫逻辑块,虽然在Minix1.0中红旗linux系统下载,逻辑块与盘块没有区别,一个逻辑块也是1KB,但在高版本的文件系统中,一个逻辑块一般表示数学上相邻的x个盘块(x是2的幂次)。在超级块中,记录一个逻辑块对应多少盘块的数组是s_log_zone_size(见上图)。由上图可知,逻辑块位图最多使用8块缓冲块(s_zmap[8]),每位缓冲块代表8192(1K*8bits)个盘块,因而Minix1.0文件系统最大支持64MB的块设备
在Linux0.11中,被加载的文件系统超级块保存在链表super_block中(定义在fs/super.c),该字段有8项,表明Linux0.11系统中最多可以同时加载8个文件系统。该字段在mount_root函数中被初始化,在read_super函数中会为新加载的文件系统设置一个超级块项,put_super函数则负责释放链表手指定的超级块项
i节点块与数据区
i节点是个结构体,用于储存设备上每位文件和目录名的相关信息,实际上文件系统中不是以直观的[文件名/目录名]的方式来组织文件层级关系,每位文件和目录都有自己对应的i节点,该结构体保存了文件和目录的一些信息,其中就包含了[文件内容/目录块]的索引信息。即文件里的实际内容及一个目录下有什么文件保存在数据区的盘块里,而[文件/目录名]对应的i节点中有数组指向数据区中实际储存数据的盘块,因而可以获取到其数据
继续以360KBc盘为例,该文件系统中有120个i节点(创建文件系统时手动初始化的),每位i节点结构体大小是32个字节,所以一共须要32*120=3840个字节的连续空间来储存这120个i节点,舍入一下就是4个盘块。对照第一张图,在数据区前确实有4个连续的盘块作为i节点块
具体的i节点结构如下(i_nlinks数组前面会讨论):
其中i_zone[9]链表就是拿来储存文件所占用的数据区中的逻辑块号。前7项是直接块号,即前7项中的每一项指向一个数据区中的盘块;第8项是一次间接块号,即该项指向数据区中的一个盘块(1KB),而该盘块中的每两个字节(注意i_zone定义的类型为short链表)又指向一个数据区中的盘块,所以i_zone中的第8项实际可以获取到数据区中的512(1024/2)个盘块;第9项是二次间接块号,与第8项稍有不同的是经一次间接后找到的盘块其又指向512个盘块,故实际上第9项可以获取到512*512个盘块。所以对于Minix1.0文件系统来说,一个文件最大宽度为(7+512+512*512)*1KB=262663KB,示意图如下:
i_mode数组也值得考究,它是一个16位的数组,用于保存文件的类型和访问权限属性(如“ls-l”命令查看到的“drw-r–r–”),其每个的含意如下:
由此可以获知,虽然我们常分辨的文件和目录在宏观上并没有多少差距,它们都是文件,只不过是不同类型的文件罢了
i节点位图块与逻辑块位图块
i节点位图用于说明对应的i节点是否被使用,1个比特位代表一个i节点,这也能够解释为何一个盘块可以表示8192个i节点的使用情况。逻辑位图块与之相像,每位比特位代表对应数据区中盘块的使用情况。这两个块有个共同点,就是它们的最低比特位(位0)都闲置不用(置为1)
对于360KBc盘中的文件系统来说,120个i节点对应121位比特位(包含闲置的最低比特位),故一个i节点位图块能够搞定。同时,360个盘块除开非数据区的盘块外一共有352个,对应353位比特位(同理),因而一个逻辑块位图块也能搞定
文件系统目录项结构
通过前面的描述我们可以获知,要想获取一个文件的内容,例如我想获取/usr/root/hello.c的内容,次序查询hello.c对应i节点的i_zone链表即可。这么问题来了,该文件对应的i节点地址又从何知晓呢?
实际上,存在一种数据结构称作目录项,其定义在include/linux/fs.h:
1
2
3
4
5
6
7
8
9
// Line 36
// Line 157
struct dir_entry {
unsigned short inode;
char name[NAME_LEN];
};
一个目录项包含最大宽度为14字节的文件名和i节点号linux文件系统的超级块,故一个目录项宽度为16字节,一个盘块最多可以储存64(1024/16)个目录项。并且根目录(/)的i节点号是确定的,为1。所以假如我想获取/usr/root/hello.c的内容,首先就去到1号i节点(根目录),该i节点的i_zone[0]中保存了根目录下所有文件的目录项(如bin、dev、root等目录文件的目录项)。通过usr这个文件名就可以在目录项中找到其对应的i节点号,从而找到/usr对应的i节点,该i节点的i_zone[0]中也保存了/usr目录下所有文件的目录项,通过文件名root找到/usr/root对应的i节点,以这种推便可找到/usr/root/hello.c对应的i节点,文件名就是这样一层层地被解析到对应i节点的
硬链接与符号链接
如今就可以来谈谈i节点中的i_nlinks数组了,有多少个文件目录项指向该i节点,i_nlinks的值就是多少,其还有一个名称称作文件的硬链接数。因为目录项的存在,造成多个不同文件名可以同时表示一个文件(只要最后的inode值相同),只有当i节点的硬链接数为0时内核才能真正从c盘上删掉该文件的数据
想必在平常“ls-a”时会发觉每位目录下都有两个特殊的目录文件‘.’和‘..’,’.’对应的目录项给出当前目录的i节点号,’..’对应的目录项给出了当前目录的父级目录的i节点号。所以对于一个目录(例如/usr/bin)来说,其硬链接数最少为2,一个是父级目录/usr的目录项中有一项指向/usr/bin的i节点,另一个则是/usr/bin目录下的‘.’。假如/usr/bin目录下还有子目录,则这种子目录的‘..’也会指向/usr/bin的i节点,故一个目录的硬链接数等于2+子目录数
另一个与之对应的名词是符号链接(ln-s),与硬链接不同,符号链接文件的数据块中储存的是作为链接对象的路径名字符串,访问符号链接文件时,内核会读取文件中的路径,之后去访问指定的文件,所以符号链接并不会降低目标文件的硬链接数
硬链接与符号链接有一个很关键的区别,那就是由于目录项中的i节点号仅能用于当前文件系统,故硬链接不能跨越文件系统。而符号链接则因其储存的是链接对象的路径名linux文件系统的超级块,故其可以不局限在一个文件系统中
高速缓冲区
块设备的访问速率与显存的访问速率相差甚远,每次文件系统在访问块设备中数据时平缓的I/O操作会对系统性能形成巨大的影响。为了提升系统性能,内核在显存中开辟了一个高速数据缓冲区,并将其界定为一个个与盘块大小相等的缓冲块来使用和管理,以期降低访问块设备的次数。
高速缓冲中储存着近来被使用过的各个块设备中的数据块,当须要从块设备中读取数据时,缓冲区管理程序会先到高速缓冲里找,假如找到了就直接用来用,否则发出读块设备命令,将数据读到高速缓冲中。当须要把数据讲到块设备中时,系统在高速缓冲中找一块空闲缓冲块对这种要写入的数据进行临时储存,直至进行设备数据同步时(如sync命令)才能真正将数据讲到块设备中
之前在main.c源码阅读的文章中给出的显存分布图:
其中的高速缓冲就是这儿提及的高速数据缓冲区
文章评论