文件管理是操作系统主要功能之一,也是难点之一。想要读懂这部分源码,就要理解文件系统的相关数据结构的设计理念,为什么要这么设计?带着这个问题我们将开启我们的揭秘旅程......
- 九阳神功残卷(一)------磁盘结构
这个是磁盘的物理结构,磁盘就是由那一个个小方块组成的,它也就是磁盘的最小存储单元------扇区。我们将所有Platter(盘面)的所有Track(磁道)拉直连接到一起,就形成如下图的结构,当然有些变化,下面的一个方格为一个盘块,大小为1K字节,相当于两个扇区,就是上图的方块(盘块和内存中的高速缓冲块大小是一样的,高速缓冲块就是盘块加载到内存后的结构,上节已经介绍过了)
这样看起来磁盘的结构好像一个盘块数组(元素下标为当前磁道*每磁道扇区数+当前扇区在当前磁道的偏移)
- 参破玄机
现在知道磁盘是这个样子,如果要我设计一套文件系统,我该怎么组织这些保存了文件的盘块呢?在此郑重声明,接下来的思路仅是个人想法,是本人在看了文件系统后反推出的Linus当时的设计思路,虽然看起来闲的有些蛋疼,但是能加深自己对文件系统源码的理解~
①在把磁盘当成数组后,很自然的会想到可以和书本一样来管理磁盘,用目录项(文件管理信息---i节点)来指明对应章节(文件),章节包含哪几页(文件存放的盘块)。但是和书本的章节不一样,书本的章节印刷好了大小(页数)就固定了,而文件的大小(盘块数)是可以修改的,如果按书本的组织方式,增加第一个文件的长度很可能会覆盖第二个文件的头部,所以要对目录项进行改造。我们可以将原先的一个目录项拆分成N个子目录项(i节点->i_zone[9]),每个子目录项表示章节的一页(文件的一个盘块),这样如果一章的内容在N页范围内(文件的大小在N*1K(盘块大小)内),修改章节(文件)的大小是不会影响其他章节的,而且保存章节(文件)的页码(盘号)也可以是不连续的。
②如果文件很大怎么保存?所以我们还要对①方案进行修改,要能动态扩展子目录项,这样我们就可以动态扩展文件长度,我们可以将N个子目录项的最后一个子目录项变成二级子目录项,即指向子目录项的子目录项(类似指向指针的指针),这个子目录项指向一个盘块,但是这个盘块放的不是文件内容,而是文件的子目录项,这样我们就拥有了(N-1+一个盘块包含的子目录项个数)个子目录项,我们只用了一个二级子目录项就能扩展出更多的子目录项,而且目录项的大小依然是固定的N个子目录项(只是最后一个是二级目录项,不过不管几级目录项大小是一致的,这就是i节点中的i_zone),于是我们还可以用数组的方式来保存所有目录项,便于查询和管理。
③文件树怎么组织?所有目录项按数组的方式保存在盘块里,其数组下标是唯一的,所以我们可以把这个下标当做是文件的标志(i节点号)。我们把文件夹当做一个特殊的文件,它保存的内容全部都是其下所有文件的数组下标,包括父目录的下标和自己的下标。
④分配盘块存储文件内容的时候,怎么能很快知道那个盘块是空闲的呢?当然你可以从第一个盘块开始一个个遍历测试,不过这个效率太慢了,很自然我们会想到位图的方式,用1Bit位来代表一个盘块的使用状态,一个字节可以表示8个盘块,所以我们只需要几个盘块作为位图索引就可以很快知道整个磁盘的所有盘块的使用状态了
- 再习神功
在YY了大神的思路后,我们再回过头来看下这幅图(只有一个C盘的效果)
这边的i节点就是保存文件头部信息,就是我前面说的目录项,逻辑块位图就是我们前面说的用于快速定位数据区盘块使用状态的MAP。当然我们还需要一个盘块来描述i节点和逻辑块位图的大小和位置,保存这些信息的就是我们的超级块,下面将详细介绍各个部分的结构。
首先来看下超级块的结构:
然后我们看下目录项(i节点)的结构:
如果前面我描述的子目录项的概念不怎么容易懂的话,这边就直接看图吧~
- 略有小成
看完这么多“推理”和数据结构后,我们即将开始学习文件系统,如果在后面遇到不懂的地方可以回头再看看前面的内容,应该会有收获~这边还是不打算给出源码的解读(这个看《Linux内核注释》就好了),我还是给出程序流程图,让小伙伴们能整体宏观,局部微观的来把握文件系统,这套流程图是以namei函数为入口来扩散解读文件系统的一些重要函数。