linux-0.11之文件系统其实很简单

这篇文章将改为从设计的角度来分析linux-0.11的文件系统这部分的代码,这样堆砌代码的操作就会很少。

第一个步骤开始。

应用层首先调用:文件描述符=open(文件名,模式)。之后库函数会将其展开,最终进入到汇编函数system_call,之后就会调用sys_open()。

可以想见,在编写sys_open()函数的时候,需要有打开字符设备与块设备文件的能力,当然目前需要打开的是文件。

可以这样的设计一个文件系统,而打开文件系统中的文件是这样的流程:

设计一个块A(超级块),它主要记录的内容:文件系统有多少个块、最大文件的大小、文件系统的魔数、块的逻辑块标志图、文件的最大个数与文件内容存放在哪些位置。

超级块的设计基本上覆盖了文件系统的全部了,剩下的是文件夹怎么去设计。它可以独立出来,而使用文件的方式去找到文件夹,文件再索引回文件。这样使用文件找到文件也没有问题,文件找到文件夹再转到文件也没有问题。通过文件夹转到文件夹然后再到文件等都是一个套路。

现在把这个结构整理一下,就是这样的。

linux-0.11之文件系统其实很简单

根据这个设计图,再来强调一下。可以将i节点表中第一个i节点设计为根的i节点。那么根目录下要么是文件,要么是目录。它可以把他们都当作目录,这就抽象出来一个结构体,内容就是目录名与i节点号。所以就从i节点能找到目录,目录结构体中包含i节点号,又转回到i节点表中。这样最终都是在i节点表做中转。

那么i节点结构体怎么去设计?

有文件类型(目录与普通文件)、文件长度、记录哪些块被占用的数组,以及该i节点被引用的次数。


普及一些基础知识:

一个块设备,例如nand flash,按照1KB分成一个块,称为逻辑块。文件系统的魔数可以看作是文件系统的id号,因为可以设计很多的文件系统,每一个文件系统必须有自己的号,这样能跟其他文件系统区分开。块的逻辑块标志图表示用位图来标志块空闲还是被占用着的。文件的最大个数放在块A(超级块),文件内容存放在哪些位置则放在i节点中。


有了上面的设计思路,再来说怎么去找到一个文件:

\u0010比如有一个文件名:/src/test.txt,怎么去读取里面的内容呢?


1.节点表中的第一个节点的属性判断为目录,其结构体中有数组项会说明占用了那个块,然后就是结构体中有说明文件(根据目前情况,其实际是目录)大小,那么遍历数组中提示的块,

就能找到src这个目录,自然也就找到src目录的节点号了。

2.根据src目录的节点号,再去节点表中搜索该节点。通过这个节点号也就找到了这个节点的整个结构体。

3.根据2中找到的i节点结构体,也知道src目录的大小以及它的内容在那个区块,所以就能遍历到test.txt文件名(现在仍然看作是目录),通过文件名也就知道test.txt的i节点号。

4.根据i节点号,再去搜索i节点表,也就知道test.txt这个节点的结构体了。

5.找到i节点结构体,也就知道test.txt的属性为文件名(不再是目录),而i节点结构体中有数组项会说明占用了哪些区块,这些区块其实就是test.txt文件的内容。所以最终通过这种方式就能读取test.txt内容了。


知道怎么找一个文件,再看怎么去写一个文件,例如写文件:/src/test.txt

假设test.txt已经存在,则根据读的方法,先找到test.txt的i节点,然后往里面写内容,之后还需要更新test.txt的i节点,因为写了新的内容之后,i节点中的文件大小字段需要更新,以及占用的块数组也需要更新。另外则是更新块区占用的位图信息以及i节点位图被占用的信息。


假设test.txt不存在,则定位到src目录下之后,需要在它的目录下新建一个目录项,名称为test.txt,节点号则需要从节点位图中找到一个空项,然后对应到节点表中,之后则是写文件内容,继续更新节点表中test.txt节点的结构体内容,之后更新块位图与i节点位图即可。


上面至说到找到与写test.txt内容,现在再来说说如果test.txt过大或者过小,怎么去设计存放它的内容的数组。


现在先设置一个数组,array[9],里面存放的都是逻辑块的地址。这样的话最大也就只能存放test.txt里9KB的数据。这么想一下,如果array[7]存放的是一个逻辑块的地址,而这个逻辑块存放了1KB/2B=512个逻辑块地址,这个意思就相当于array[6]的内容,那这样就多存放512KB数据。依次类推一下,如果array[8]存放的块的地址里面的512项内容与array[7]相同,相当于每个有512个块,所以整个又能多存放512*512KB的数据。


至此,这样的结构就能存放7KB+512KB+512*512KB的数据。


如果对上面找一个文件的流程真正的理解了,那在框架上对文件系统也就掌握的很好了。


下面就开始介绍在代码角度怎么去合理编写程序(先脱离linux-0.11内核整体的代码结构):

打开文件/src/test.txt:

1.编写一个总览性质的函数,功能就是打开文件名:/src/test.txt。对于用户空间程序,只需要知道内核返回过来的文件描述符数就好。说到这里,再额外说一下,进程使用一个task_struct结构体来表示的,里面有一个文件结构体(对应内核一个文件结构体数组),文件结构有节点。所以整个下来,给用户空间返回一个描述符,其实就是结构体数组中的一个位置值。可以命名这个框架的函数名为:int sys_open(const char * filename,int flag,int mode)。

这个函数返回的就是描述符(数组中没被占用的位置值)。

2.寻找文件,找到/src/test.txt文件的i节点(其实就是test.txt节点),这是实现上面框架的最重要的函数,它也是一个框架函数,需要返回i节点结构体。命名为:

<code>sys_open   --->open_namei      --->dir_namei         --->get_dir            --->find_entry            --->iget      --->find_entry      --->iget/<code>


3.步骤2中的函数第一个需要找到目录src,也就是最终的test.txt上一层目录,因为test.txt与目录还是有差别的,所以再建立一个函数,做到这个步骤就可以停下了,命名为:

<code>static struct m_inode * dir_namei(const char * pathname, int * namelen, const char ** name)/<code>

再多说一句,比如上面找的是/src/a/test.txt,那这个函数相当于找到/src/a这个位置就可以停下来了。就是说找到目录a的节点就可以返回了。


4.找目录这个过程,再细分一下,它只需要一个路径名参数即可。

<code>static struct m_inode * get_dir(const char * pathname)/<code>

最后返回的就是test.txt上一层目录的i节点结构体。


5.找目录实际需要根据路径名追踪。从i节点表中获取类型,是目录则先根据zone数组获取存放目录结构体的块,然后匹配目录src,一直匹配下去,直到没有找到符号/,也就是只能找到test.txt上一个目录。可以命名这个最终干苦力的函数:

<code>static struct buffer_head * find_entry(struct m_inode ** dir,const char * name, int namelen, struct dir_entry ** res_dir)/<code>

它需要返回找到的最后一个目录的块,并且返回找到的这个目录的i节点。


找i节点也是一个苦力活,可以通过设备号与i节点值(如第2个i节点)来找。命名函数:

<code>struct m_inode * iget(int dev,int nr)/<code>

它最主要就是在硬盘上的i节点的节点表中去找。当然,为了整个系统的效率,可以设置一个内存数组存放以前读取过的i节点数组,这样可以先从数组中找,然后最次才是从硬盘中找。


所以,find_entry()与iget()配合,先使用find_entry()通过目录名找到i节点号,这样就能使用iget()去i节点表(存于硬盘上)中找到i节点。这样循环往复,就可以定位到test.txt上一层目录节点了。


6.上面找到了目录src的节点,就可以通过目录节点找到test.txt这个目录(文件名+i节点号),这样再从i节点表中就可以找到test.txt这个文件了。所以只需要调用find_entry()与iget()结合一次就可以完成任务了。


总结一下整个函数的关系:

<code>sys_open     --->open_namei        --->dir_namei         --->get_dir            --->find_entry            --->iget      --->find_entry      --->iget/<code>

至于写文件,相信理解了上面写的流程,分析起来很简单了。

文件系统就先分析这么多,还有很多内容没有说,例如为了提高读写效率,会设计中间缓存buffer。总体来说,minix文件系统组织方式并不复杂。

另外,该系统只是理论上的文件系统,它还有非常多的缺陷。例如,文件系统的完整性不能保证、磨损均衡同样没有。


分享到:


相關文章: