14.4 inode

接下来我们看一下磁盘上存储的inode究竟是什么?首先我们前面已经看过了,这是一个64字节的数据结构。

  • 通常来说它有一个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,它对应了磁盘上一个block,这个block包含了256个block number,这256个block number包含了文件的数据。所以inode中block number 0到block number 11都是direct block number,而block number 12保存的indirect block number指向了另一个block。

以上基本就是XV6中inode的组成部分。

基于上面的内容,XV6中文件最大的长度是多少呢?

学生回答:会是268*1024字节

是的,最大文件尺寸对应的是下面的公式。

可以算出这里就是268KB,这么点大小能存个什么呢?所以这是个很小的文件长度,实际的文件系统,文件最大的长度会大的多得多。那可以做一些什么来让文件系统支持大得多的文件呢?

学生回答:可以扩展inode中indirect部分吗?

是的,可以用类似page table的方式,构建一个双重indirect block number指向一个block,这个block中再包含了256个indirect block number,每一个又指向了包含256个block number的block。这样的话,最大的文件长度会大得多(注,是256*256*1K)。这里修改了inode的数据结构,你可以使用类似page table的树状结构,也可以按照B树或者其他更复杂的树结构实现。XV6这里极其简单,基本是按照最早的Uinx实现方式来的,不过你可以实现更复杂的结构。实际上,在接下来的File system lab中,你将会实现双重indirect block number来支持更大的文件。

学生提问:为什么每个block存储256个block编号?

Frans教授:因为每个编号是4个字节。1024/4 = 256。这又带出了一个问题,如果block编号只是4个字节,磁盘最大能有多大?是的,2的32次方(注,4TB)。有些磁盘比这个数字要大,所以通常人们会使用比32bit更长的数字来表示block编号。

在下一个File system lab,你们需要将inode中的一个block number变成双重indirect block number,这个双重indirect block number将会指向一个包含了256个indirect block number的block,其中的每一个indirect block number再指向一个包含了256个block number的block,这样文件就可以大得多。

接下来,我们想要实现read系统调用。假设我们需要读取文件的第8000个字节,那么你该读取哪个block呢?从inode的数据结构中该如何计算呢?

学生回答:首先应该减去12个direct block的大小,然后再看在indirect block中的偏移量是多少。

对于8000,我们首先除以1024,也就是block的大小,得到大概是7。这意味着第7个block就包含了第8000个字节。所以直接在inode的direct block number中,就包含了第8000个字节的block。为了找到这个字节在第7个block的哪个位置,我们需要用8000对1024求余数,我猜结果是是832。所以为了读取文件的第8000个字节,文件系统查看inode,先用8000除以1024得到block number,然后再用8000对1024求余读取block中对应的字节。

总结一下,inode中的信息完全足够用来实现read/write系统调用,至少可以找到哪个disk block需要用来执行read/write系统调用。

接下来我们讨论一下目录(directory)。文件系统的酷炫特性就是层次化的命名空间(hierarchical namespace),你可以在文件系统中保存对用户友好的文件名。大部分Unix文件系统有趣的点在于,一个目录本质上是一个文件加上一些文件系统能够理解的结构。在XV6中,这里的结构极其简单。每一个目录包含了directory entries,每一条entry都有固定的格式:

  • 前2个字节包含了目录中文件或者子目录的inode编号,

  • 接下来的14个字节包含了文件或者子目录名。

所以每个entry总共是16个字节。

对于实现路径名查找,这里的信息就足够了。假设我们要查找路径名“/y/x”,我们该怎么做呢?

从路径名我们知道,应该从root inode开始查找。通常root inode会有固定的inode编号,在XV6中,这个编号是1。我们该如何根据编号找到root inode呢?从前一节我们可以知道,inode从block 32开始,如果是inode1,那么必然在block 32中的64到128字节的位置。所以文件系统可以直接读到root inode的内容。

对于路径名查找程序,接下来就是扫描root inode包含的所有block,以找到“y”。该怎么找到root inode所有对应的block呢?根据前一节的内容就是读取所有的direct block number和indirect block number。

结果可能是找到了,也可能是没有找到。如果找到了,那么目录y也会有一个inode编号,假设是251,

我们可以继续从inode 251查找,先读取inode 251的内容,之后再扫描inode所有对应的block,找到“x”并得到文件x对应的inode编号,最后将其作为路径名查找的结果返回。

学生提问:有没有一些元数据表明当前的inode是目录而不是一个文件?

Frans教授:有的,实际上是在inode中。inode中的type字段表明这是一个目录还是一个文件。如果你对一个类型是文件的inode进行查找,文件系统会返回错误。

很明现,这里的结构不是很有效。为了找到一个目录名,你需要线性扫描。实际的文件系统会使用更复杂的数据结构来使得查找更快,当然这又是设计数据结构的问题,而不是设计操作系统的问题。你可以使用你喜欢的数据结构并提升性能。出于简单和更容易解释的目的,XV6使用了这里这种非常简单的数据结构。

Last updated