文件系统

1. linux文件系统

1.1 ext2文件系统

我们可以把一个磁盘分成一个或多个分区,每个分区可以包含一个文件系统。下图是一个磁盘分区格式化成ext2文件系统后的存储布局。 
文件系统
- 文件系统中存储的最小单位是块(Block),一个块究竟多大是在格式化时确定的,例如mke2fs的-b选项可以设定块大小为1024、2048或4096字节。而上图中启动块(BootBlock)的大小是确定的,就是1KB,用来存储磁盘分区信息和启动信息,任何文件系统都不能使用启动块。 
- 启动块之后才是ext2文件系统的开始,ext2文件系统将整个分区划成若干个同样大小的块组(Block Group),每个块组都由以下部分组成。

点击查看关于启动块的介绍和管理的文章

  • 超级块(Super Block) 描述整个分区的文件系统信息,例如块大小、文件系统版本号、上次mount的时间等等。超级块在每个块组的开头都有一份拷贝。
  • 块组描述符表(GDT,Group Descriptor Table) 由很多块组描述符组成,整个分区分成多少个块组就对应有多少个块组描述符。每个块组描述符(Group Descriptor)存储一个块组的描述信息,例如在这个块组中从哪里开始是inode表,从哪里开始是数据块,空闲的inode和数据块还有多少个等等。和超级块类似,块组描述符表在每个块组的开头也都有一份拷贝,这些信息是非常重要的,一旦超级块意外损坏就会丢失整个分区的数据,一旦块组描述符意外损坏就会丢失整个块组的数据,因此它们都有多份拷贝。通常内核只用到第0个块组中的拷贝,当执行e2fsck检查文件系统一致性时,第0个块组中的超级块和块组描述符表就会拷贝到其它块组,这样当第0个块组的开头意外损坏时就可以用其它拷贝来恢复,从而减少损失。
  • 块位图(Block Bitmap) 一个块组中的块是这样利用的:数据块存储所有文件的数据,比如某个分区的块大小是1024字节,某个文件是2049字节,那么就需要三个数据块来存,即使第三个块只存了一个字节也需要占用一个整块;超级块、块组描述符表、块位图、inode位图、inode表这几部分存储该块组的描述信息。那么如何知道哪些块已经用来存储文件数据或其它描述信息,哪些块仍然空闲可用呢?块位图就是用来描述整个块组中哪些块已用哪些块空闲的,它本身占一个块,其中的每个bit代表本块组中的一个块,这个bit为1表示该块已用,这个bit为0表示该块空闲可用。为什么用df命令统计整个磁盘的已用空间非常快呢?因为只需要查看每个块组的块位图即可,而不需要搜遍整个分区。相反,用du命令查看一个较大目录的已用空间就非常慢,因为不可避免地要搜遍整个目录的所有文件。
  • inode位图(inode Bitmap) 和块位图类似,本身占一个块,其中每个bit表示一个inode是否空闲可用。
  • inode表(inode Table) 我们知道,一个文件除了数据需要存储之外,一些描述信息 
    也需要存储,例如文件类型(常规、目录、符号链接等),权限,文件大小,创建/修改/访问时间等,也就是ls -l命令看到的那些信息,这些信息存在inode中而不是数据块中。每个文件都有一个inode,一个块组中的所有inode组成了inode表。
  • 数据块(Data Block) 根据不同的文件类型有以下几种情况

    1. 对于常规文件,文件的数据存储在数据块中。
    2. 对于目录,该目录下的所有文件名和目录名存储在数据块中,注意文件名保存在它所在目录的数据块中,除文件名之外,ls -l命令看到的其它信息都保存在该文件的inode中。
    3. 对于符号链接,如果目标路径名较短则直接保存在inode中以便更快地查找,如果目标路径名较长则分配一个数据块来保存。 
      4.* 设备文件、FIFO和socket等特殊文件*没有数据块,设备文件的主设备号和次设备号保存在inode中。

1.2数据块寻址

inode中存着许多inode,每个inode占128B,每个inode存放着文件属性和数据块指针,数据块指针呢占60B。下图表示如何根据inode找到数据块。 
文件系统
inode进行寻址有两种方式: 
- 直接寻址,也就是inode记录了数据存放的数据块的编号。 
- 间接寻址,间接寻址又分成一级间接寻址,二级间接寻址,三级间接寻址。以一集间接寻址为例,inode记录的是一个数据块编号,这个数据块不存放数据,而是分成若干个(数据块大小/4B个)记录,每个记录存放数据块的编号,这些数据块存放数据。通过这么一级一级的扩大数据存放范围。

举一个例子,假如一个数据块Block大小为4KB,每个数据块编号为4B,那么每个Block就可以寻址4KB/4B = 1K个数据块。

  • 12个直接寻址——> 12*4KB = 48KB
  • 一级间接寻址——> 1K * 4KB = 4MB
  • 二级间接寻址——> 1K * 1K * 4KB = 4GB
  • 三级间接寻址——> 1K * 1K * 1K * 4KB = 4TB

这样,一个inode就可以寻址一个48KB+4MB+4GB+4TB大小的文件,已经足够了。