当初上课上的迷迷糊糊,最近我查阅了一些资料,将文件系统中的基础知识有逻辑的整理出来。
- 什么是文件?
- unix中的目录是什么?
- 如何理解硬链接和软链接?(文件共享)
- 目录中是如何检索文件的?
- 文件的逻辑结构和物理结构?或者说文件再磁盘上是如何被组织起来的?
- 空闲文件的组织与管理?
- 磁盘的组织与管理?
什么是文件?
提到文件,大多数人的脑子中肯定首先浮现的是pdf、jpg,mp3等文件。他们确实是文件,但是你脑子中所想的只是文件的文件体,也就是文件真正的数据(一些二进制流)所在。
那么要对文件进行管理,只有这些数据不够,还需要一些描述信息(文件名、文件内部标识、文件存储地址、访问权限、访问时间),这些描述信息可以称为文件说明,也可以称为文件控制块(FCB)。
所以unix中的文件 = 文件体 + 文件描述信息。
下面思考一个问题:
XV6和所有的Unix文件系统都支持通过系统调用创建链接,给同一个文件指定多个名字。你可以通过调用link系统调用,为之前创建的文件“x/y”创建另一个名字“x/z”。即使在windows中也能有创建快捷方式,将同一个文件重命名并存放在不同的位置。而两个文件访问的内容确是一模一样的。
所以,在文件系统内部,文件描述符必然与某个对象关联,而这个对象不依赖文件名。
那么这个对象就是大家熟知的inode(index node,索引结点)。inode是什么,直接拿inode的数据结构来看是最好不过的了。除此之外,系统会为每一个inode分配一个唯一的编号。
inode的数据结构:
- 通常来说它有一个type字段,表明inode是文件还是目录。
- nlink字段,也就是link计数器,用来跟踪究竟有多少文件名指向了当前的inode。
- size字段,表明了文件数据有多少个字节。
- 不同文件系统中的表达方式可能不一样,不过在XV6中接下来是一些block的编号,例如编号0,编号1,等等。XV6的inode中总共有12个block编号。这些被称为direct block number。这12个block编号指向了构成文件的前12个block。举个例子,如果文件只有2个字节,那么只会有一个block编号0,它包含的数字是磁盘上文件前2个字节的block的位置。
- 之后还有一个indirect block number(当文件数据过于大的时候,用于扩展,此处我们不详细讨论它)
从上面可以知道,inode中存了文件体(也就是文件真实的数据)的地址,通过inode找到文件内容。这样一层封装,使得不同的文件名都可以映射到这个inode上来,从而找到文件数据。
可是,文件名去哪了呢?inode中的内容很像上面讲的文件说明(文件描述信息),其实正是。不过恰恰缺了文件名。而文件名和inode编号之间必然有一个映射表,这张表又在哪里呢?答案就在目录!
unix中的目录是什么?
目录就是一个文件。
一下子可能无法理解,还是从你的思想出发,提到目录你可能想到一个文件夹内部的结构。就大概这么一张图,也就是说目录内部应该包括一些文件(此处的文件指的就是一般的文件如下图中的mmap文件)和子目录(下图中的存储管理、IO、基础知识都还是一个目录)。
接下来就好理解了,目录是一个文件,文件体(数据)中存的就是这些文件/子目录的文件名和inode编号的映射表!
也就是说,对于每个文件而言,其文件名和真实的数据是存储在不同的物理位置上的。
举个经典例子,通过路径查找文件的过程:/home/alex/main.py
/可以理解为根目录,根目录因为是固定的,所以其inode的位置是系统已知的。那么先找到root的inode的编号(此处需要说明可以通过inode编号直接计算出inode的地址,具体计算方式不在此赘述),读取其信息,其那些文件体的地址,然后找到文件体,发现里面都是文件名/子目录名与inode编号的映射关系,逐一遍历文件名(之后在检索中还有其他方式,此处为线性检索)然后找到匹配的“home”,查到它的inode编号,获取其inode中的内容,然后找到真实的数据。以此类推,找到alex目录文件的inode编号,继而再找到main文件的inode编号。此时,main文件的inode中的指针指向的就是main.py真实的数据。
有一个非常有趣的现象,就是文件名是由其目录所存储的。而root文件没有上一级目录了,所以root是没有文件名,或者说是一个空的文件名,只用一个/表示。/home/alex/main.py可以理解为空/home/alex/main.py。
如何理解硬链接和软链接?
理解了inode概念,再来理解硬链接就不要太容易了!
硬链接感性的理解,就是为同一个inode取了一个新名字,这样新名字和旧名字对应的inode编号都是同一个文件。那么为什么要设计硬链接呢?当然是因为要共享咯!同时需要注意的是,在inode中有个属性叫做nlink,当有一个新的硬链接时,inode会将这个值加一,表明这个文件在被其他人共享。当删除这个硬链接时,inode会判断nlink是否为1,若为1,则表明是这个文件最后一个主人要把我删除,那么在删除硬链接的同时,数据也会被删除;否则,直接将nlink减一即可。
硬链接存在的问题:文件的创建者不能删除文件。因为文件被共享之后,只有文件的最后一个所有者才能删除文件。
此处思考一个有趣的现象,是我自己的推测。为什么不直接将所有的文件描述存在目录文件中呢?也就是说能不能让目录文件中的目录项为文件名与文件描述(原先inode中存储的信息)的映射呢?这样就不要先通过文件名找到inode,再通过inode找到文件描述了。
考虑一下,如果此时出现大量的硬链接,而且还是对同一个文件的,那么文件的文件描述就会被重复存储。(数据库中称之为传递依赖)那么将这些文件描述分离出去,当做一个inode存储,在原表中放置inode的编号。
引入软链接,可以解决上述的问题。软链接其实是存储了被共享文件的路径名,这个路径名可以是绝对路径也可以是相对路径。所以软链接的inode和被共享文件的inode不同,当通过软链接查找文件时,其实是先获取到被共享文件的路径名,然后循着这个路径查找文件。这样一来,若文件的创建者将文件删除,那么即使有着这个路径名也无法查找到对应的文件。
同时,软链接也存在一些问题,就是每次都得通过路径逐层查找,开销较大。
目录中是如何检索文件的?
首先解答一个问题,就是目录的结构是怎样的?我们现在可能很好回答,应该是树状结构的。没错,但是目录结构最开始有单级目录结构、二级目录结构。再是树状结构,图状结构的。
单级目录结构就是将所有的目录项堆在一个表中。二级目录结构是按照用户分了一级出来,每个用户自己的目录项还是堆在一个表中。树状结构是对二级目录结构的推广,或者可以称之为多级目录结构。
检索文件的方式:线性表、哈希表。线性表之前已经论述过了,哈希表也就是散列存储,通过文件名直接输入到散列函数中即可得到inode编号。
未完待续...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。