文件系统笔记九、Ext2文件系统布局及核心数据结构

文件系统笔记九、Ext2文件系统布局及核心数据结构

引言:在之前的笔记中,我们依次介绍了磁盘的物理结构、文件系统设计需要考量的因素、文件系统性能及保障机制。这些是对文件系统泛泛的介绍,这篇笔记我们将简单剖析Ext2文件系统,重点介绍其系统布局及核心数据结构。


一、Ext2文件系统布局

  我们知道,磁盘是持久性的圆形存储介质,文件系统是操作系统为磁盘做的一层易于使用的抽象,帮助用户管理和组织磁盘中的文件。本文将简单剖析Ext2文件系统,介绍其系统布局和核心数据结构。下图为ext2文件系统的存储布局。

这里写图片描述
图1、ext2文件系统存储布局

  磁盘中最小存储单位是扇区,其大小在(1扇区 = 512byte或4K),参考4K对齐。而文件系统最小管理单元是Block(一般为4K)。扇区大小在磁盘出厂就已经确定了,而Block大小是在格式化时决定的。

  为便于解释清楚文件系统的布局,分行对相关概念阐述,解释。

1.1、固定部分

在具体介绍前两行之前,我们回想一下计算机的启动过程:
1)、上电后,处在主板ROM里面的BIOS程序首先启动,BIOS在进行一些基本的系统配置扫描后,对磁盘的扇面0进行读操作,将MBR里面的程序读到内存并运行。
2)、MBR程序接下来找到主分区,将主分区里面的Boot Sector加载并运行。
3)、Boot Record里面的内容是一个小程序,该程序负责找到操作系统的镜像,并加载到内存,从而启动操作系统。

这里写图片描述
图2、前两行

1)、MBR(Master Boot Record, 主引导记录):它位于磁盘的扇面0,作用是检查分区表是否正确及确定哪个分区为基本的引导分区,并在程序结束后,把主分区的启动系统调入内存加以执行。MBR后面的分区文件系统可以是NTFS、Ext2、Ext3等等。

扫描二维码关注公众号,回复: 2103457 查看本文章

2)、分区Partition1 - 4:各分区包括Boot Sector启动块及具体的文件系统。

那为什么要分区呢?容易想到的理由有
1、分区方便我们使用磁盘,因为不同分区可以建立不同的文件系统
2、分区有安全上的优势,因为一个分区损坏了,另外一个分区依然可以使用
3、分区还有可靠性优势,一个分区不影响另外一个分区运行

真正的核心是因素是:计算机内存字长度通常有限,而磁盘地址需要存放在内存字里面,这样操作系统能够访问的磁盘地址数量就是一个有限数,分区是为充分利用磁盘空间。

3)、启动块Boot Sector:有时也称Boot Record,是用来记录磁盘分区和启动信息,任何文件系统都不能缺少启动块,启动块大小并不是我们前面说的4KB,而是1KB,这个是PC变准定义的。

疑问1、如果不是主分区,还有启动块吗?
  主分区是一个比较单纯的分区,通常位于硬盘的最前面一块区域中,构成逻辑C磁盘。其中的主引导程序是它的一部分,此段程序主要用于检测硬盘分区的正确性,并确定活动分区,负责把引导权移交给活动分区的DOS或其他操作系统。由于硬盘的主引导记录中仅仅为分区表保留了64个字节的存储空间,而每个分区的参数占据16个字节,故主引导扇区中总计只能存储4个分区的数据。故可以判定仅主分区,存在启动块。

4)、Ext2 File System:启动块之后才是ext2文件系统的开始,ext2文件系统将这个分区划分为大小相等的块组(Block Group),每个块组包括Super Block超级块、GDT组描述符、Block Bitmap块位图、Inode Bitmap Inode位图、DataBlock数据块.下面将依次介绍他们的作用。


1.2、Block Group块组内容:

  在Boot Sector启动块之后,是ext2文件系统的开始,ext2文件系统将这个分区划分为大小相等的块组(Block Group)。这里依次介绍每个其含义及数据结构:

这里写图片描述
图3、块组内容

1)、Super Block超级块:超级块给出了文件系统的全局信息(如块大小,文件系统的版本等);另外超级块结构包含一些函数指针,例如super_operation的成员函数read_inode提供了读取inode信息的功能。

struct super_block {
    unsigned long s_blocksize;//指定了文件系统的块大小
    unsigned char s_blocksize_bits;
    ……/*省略超级块的链表、设备号等代码*/
    unsigned long long s_maxbytes; /* 指定文件系统中最大文件的尺寸 */

    struct file_system_type *s_type;/*s_type是指向file_system_type结构的指针。*/
    struct super_operations *s_op;

    unsigned long s_magic;
    struct dentry *s_root;/*s_root是指向文件系统根dentry的指针。*/

    struct list_head s_inodes; /* all inodes */
    struct list_head  s_dirty; /* dirty inodes */

    struct block_device *s_bdev;
    void  *s_fs_info; /* Filesystem private info */
 };

2)、GDT(Group Descriptor Table)组描述符表:由多组描述符组成,整个分区分成多少个组就对应多少个组描述符。每个组描述符(Group Descriptor)存储一个组的描述信息,例如这个组中从哪里是inode表、哪里开始是数据块、空闲的inode和数据块还有多少等。

  从上图可以看出,超级块和组描述符被复制到每个块组中(也看过一篇文章说只是拷贝几份),因为一旦超级块意外损坏就会丢失整个分区的数据,一旦块组描述符意外损坏就会丢失整个块组的数据,因此它们都有多份拷贝
  但是只有块组0中包含的超级块和组描述符才被内核使用,当执行e2fsck检查文件系统一致性时,第0个块组中的超级块和块组描述符表就会拷贝到其它块组,这样当第0个块组的开头意外损坏时就可以用其它拷贝来恢复,从而减少损失。

3)、Block Bitmap,块位图:我们之前介绍过位图法表示剩余空间,块位图就是用来描述整个块组中哪些块已用哪些块空闲的,它本身占一个块,其中的每个bit代表本块组中的一个块,这个bit为1表示该块已用,这个bit为0表示该块空闲可用。

1.为什么用df命令比du命令统计整个磁盘的已用空间非常快呢?
  因为df命令只需要查看每个块组的块位图即可,而不需要搜遍整个分区。相反,用du命令查看一个较大目录的已用空间就非常慢,因为不可避免地要搜遍整个目录的所有文件。df命令用于显示磁盘上的可使用的磁盘空间。du命令是对文件和目录磁盘使用的空间的查看。

2. 在格式化一个分区时究竟会划出多少个块组呢?
  格式化一个分区有多少个块组,主要取决于分区大小和块的大小。因为Block Bitmap占一个块,即4K字节,其有4K * 8bit,可以标注32K个块,每个块的大小为4K, 即标注区域为32K * 4K = 128MB,即一个块组Group的上限为128M。若分区为32G,则ext2文件系统需要32 G / 128M = 256个块组。

4)、inode Bitmap,inode位图:与块位图类似,本身占一个块,其中每个bit表示inode是否空闲可用。通过df - i可以查看i-node基本使用情况。

5)、inode Table,inode表:

这里写图片描述
图4、inode表内容

inode表内容:我们知道,一个文件除了数据需要存储以外,一些属性信息也需要存储,如文件类型、权限、属主、创建/修改/访问的时间等,即ls -l看到的那些信息,这些信息存在inode中而不是数据块中。
inode表大小:每个文件都有一个inode,一个块组中所有inode组成inode表。在ext2文件系统中,inode的大小为128B。inode表占多少块在格式化时就要决定并写入块组描述符中,mke2fs格式化工具的默认策略是一个块组有多少个8KB就分配多少个inode,由于数据块占了整个块组的绝大部分,可以近似认为数据块有多少个8KB就分配多少个inode。故inode所占数据块近似估计为:数据块大小 / 8KB * 128B /4KB

  如果平均每个文件的大小是8KB,当分区满的时候inode表会得到比较充分的利用,数据块也不浪费。如果这个分区存的都是很大的文件(比如电影),则数据块用完时inode会有浪费(使用较少Inode),如果分区存放的都是小文件(比如源代码),则可能数据块还没有用完,则Inode已经用完了,数据块可能有很大浪费。如果用户在格式化时,能够对这个分区以后存储的文件大小做一个预测,可以用mke2fs的-i参数手动制定每多少个字节分配一个inode。

6)、Data Block:数据块: 数据块根据文件类型不同有以下几种情况
常规文件:文件的数据保存在数据块中
目录文件:该目录下的所有文件名和目录名存储在数据块中,除文件名外,其他信息ls -l命令看到的其他信息保存在该文件的i-node中,注意目录也是一种文件,是特殊文件。
符号链接:如果目标路径名较短则直接保存在inode中,以便快速查找,如果目标路径名较长则分配一个数据块来保存。
设备文件、FIFO和socket等特殊文件:没有数据块。这里面引出三个问题:

问题1、要存一个hello的文件,具体步骤是?
1)内核加载块组0中的GDT,从GDT中找出inode bitmap,从inode bitmap中找出inode table中空闲的inode。
2)申请一个inode。inode主要包含两部分内容:文件属性(68Bytes),数据块指针(60Bytes)。数据块指针指向存储hello文件目录项和文件内容的Data Block。
3)将文件内容和文件的目录信息分别存在对应的Data Block中。
4)修改对应的inode Bitmap 和 Block Bitmap。

问题2、给定文件路径“/home/hello”,操作系统时如何找到该文件的位置?
1)查找根目录的目录项。Linux有规定,根目录的目录项必须存放在2号inode中。
2)根目录的目录项中存着根目录下的子目录目录项和文件的数据块信息。通过根目录的目录项可以找到home对应的inode。
3)根据home对应的inode找到home的目录项。
4)在home目录项中找到hello文件的inode。
5)根据hello文件的inode中的数据块指针找到存储有hello文件内容的数据块。

问题3、如何删除hello文件?
1)找到hello文件位置。
2)将Block Bitmap中对应bit置为0
3)将inode Bitmap中对应bit置为0


二、数据块寻址过程

  我们在文件系统笔记六、文件系统布局及数据存储介绍了非对称多级索引的数据存储方式,ext2文件系统即采取了这样的策略,下面是其数据块寻址过程。

这里写图片描述
图5、数据块寻址过程

  上文我们说到一个inode中数据块指针占了60Bytes,其中每一个指针占4字节,一共有60/4=15个指针。从上图可以看出,索引项Blocks[12-14]分别指向一级,二级,三级的间接寻址块。因此对于一个inode来说,最多可存储(12+256+256平方+256三次方)*4KB的数据。

  这种寻址方式对于访问不超过12个数据块的小文件是非常快的,访问文件中的任意数据只需要两次读盘操作,一次读inode(也就是读索引项)一次读数据块。而访问大文件中的数据则需要最多五次读盘操作:inode、一级间接寻址块、二级间接寻址块、三级间接寻址块、数据块。实际上,磁盘中的inode和数据块往往已经被内核缓存了,读大文件的效率也不会太低。其兼顾了读大文件及小文件的能力。


参考资料:
1、Ext2文件系统简单剖析
2、02-Linux系统编程-文件系统

纠错与建议
邮箱:[email protected]


猜你喜欢

转载自blog.csdn.net/xd_hebuters/article/details/79574902
今日推荐