linux ext文件系统


我们知道linux下的一切都抽象为文件,文件大都保存在磁盘中。那么磁盘中的文件数据是如何组织和存储的呢? 我们在前面通过各种方式查看了文件的各种属性,包括文件类型、文件权限、文件所有者、文件所属用户组、文件时间、文件大小、文件名等。 这些文件属性信息在磁盘中是如何组织和存储的呢?这就是本篇要介绍的linux文件系统了。
文件系统通俗的说就是磁盘分区的格式。我们平时所说的将硬盘分区格式化,其实就是把分区格式化为某种文件系统。例如在windows下常用文件系统为FAT32、NTFS。 而linux下常用的文件系统有EXT2、EXT3、EXT4、XFS等。 较早之前的linux发行版大都默认采用EXT2文件系统,而现在最新的centos默认的文件系统为XFS文件系统。
因为文件系统是以磁盘为载体,所以我们首先需要了解磁盘的结构和分区。

一.linux文件系统

回到顶部
传统的文件系统与磁盘分区是一一对应的关系,即每个磁盘分区都会被格式化为一种文件系统格式,一个文件系统就是一个分区。 但是随着LVM和软磁盘阵列(software raid)技术的出现,这种情况发生了很大的变化。 LVM可以将一个分区格式化为多个文件系统,而软磁盘阵列(software raid)可以将多个分区合并为一个文件系统。
本文先以传统的一个分区对应一个文件系统的方式进行讲解,先忽略LVM和RAID。 关于LVM和RAID我们会在后面详细介绍,具体参考:
我们在挂载数据的时候,是以文件系统为单位进行挂载。
linux下的文件系统有很多种,大部分文件系统都是由superblock、inode、data block组成。文件内容主要存放在data block中,而文件的属性信息主要存放在inode中。
超级块superblock:超级块superblock主要记录了文件系统filesystem的整体信息,包括 inode/block 的总量、使用量、剩余量, 以及文件系统的 格式与相关信息等;
inode:inode主要用来记录文件的属性信息,同时记录了文件的内容所在的block编号,用于索引文件内容。每个文件对应一个inode,每个inode对应一个inode编号;
data block:data block存放了文件的实际内容,一个文件至少占用一个block,若文件太大,会占用多个block。每个block也对应一个block编号。
inode除了记录文件的属性信息外,也记录了文件内容所在的block编号,所以就能够通过一个文件的inode找到这个文件的实际内容了。 inode记录了文件内容的所有block编号,所以在读取文件的时候,只要知道了文件的inode信息,就能一次性快速定位文件内容的所有block, 这种存取方式称为索引式文件系统(indexed allocation),这种存取方式效率比较高,一般linux下的文件系统大部分都是这种索引式文件系统。
还有一种存取方式称为链式存取,每个文件block中存放了下一个block的编号,这样只要找到了文件的第一个block,就可以按照顺序读取后面的block了。 由于不能一次性读取文件的所有block,所以文件的存取效率就没有索引式文件系统高了。这种存取方式常见的就是windows下的FAT文件系统了, 如果文件系统使用太久,经常进行文件删除编辑新增等操作,那么可能会造成文件的block太过分散,就会出现文件碎片的问题。 磁盘的磁头无法在磁盘转动一圈就读到所有的数据,可能需要多转几圈才可以完整地读完整个文件内容。 这样文件的读取效率就会大大降低,所以就需要定期进行碎片整理,将分散的block整理到一起从而提高文件存取效率。这也是我们在windows系统加经常要做的工作了。
而索引式文件系统就很少存在这样的问题了。

二.linux EXT2文件系统

回到顶部
文本以为传统的EXT2文件系统为例,介绍文件系统的组成结构。 我们前面介绍了大部分文件系统都是由由superblock、inode、data block组成 inode 的内容在记录文件的权限与相关属性,至于 block 区块则是在记录文 件的实际内容。EXT2文件系统也不例外。 我们知道,一个磁盘可以划分成多个分区,每个分区都需要先用格式化工具mkfs命令格式化成某种格式的文件系统,然后才能存储文件, 格式化的过程就会在磁盘上写一些管理存储布局的信息。 文件系统中存储的最小单位是块(Block),一个块究竟多大是在格式化时确定的,例如mke2fs的-b选项可以设定块大小为1024、2048或4096字节。 我们在对磁盘分区格式化后,文件系统的inode与block规划固定好了, 除非用resize2fs命令变更文件系统的大小,否则inode与block这些是不会随便变化的。
Ext2文件系统组成结构如下图所示: ext2文件系统总体存储布局 图.ext2文件系统结构图
Boot Sector :每个分区最前面都有一个引导区,被主引导找到。
Super Block(超级块): 存放文件系统的结构信息, 说明各部分的大小. super_block[8], 可加载8个文件系统
Inode Bitmap(i节点位图): 记录i节点的使用情况, 1bit代表一个i节点. s_imap[8], 占用8个块, 可表示8191个i节点情况
Block Bitmap(逻辑块位图): 记录数据区的使用情况, 1bit代表一个盘块(block). s_zmap[8], 占用8个块, 最大支持64M的硬盘
Inode(i节点) Table: 存放inode 号和数据块的对应关系。每个文件或目录名唯一对应一个i节点, 在i节点中, 储存 id信息, 文件长度, 时间信息, 实际数据所在位置等等。
目录文件的data block 中存放的是目录下文件名和对应的 inode 号。
我们知道整个磁盘的第一个扇区为启动引导扇区MBR,开机启动的时候会先加载执行MBR中的引导程序。 而每个文件系统的最前面也有一个启动引导块(boot Block),启动块(Boot Block)的大小是确定的,就是1KB,启动块是由PC标准规定的, 用来存储磁盘分区信息和启动信息,任何文件系统都不能使用启动块。
每个启动引导块都可以独立安装操作系统加载程序(bootloader). 这样就可以在一块磁盘上同时安装多个操作系统,开机启动的时候MBR会根据用户指引引导到不同的文件系统启动引导块,从而启动不同的操作系统。 关于开机启动多重引导的详细原理参考这里:
启动块之后才是ext2文件系统的开始,ext2文件系统将整个分区划成若干个大小相同的块组(Block Group), 如果文件系统高达数百GB, 那么将所有的inode与block放在一起将是很不明智的决定,因为inode与block的数量太庞大,不容易管理。 因此Ext2文件系统在格式化的时候整个分区划分成若干个大小相同的块组(Block Group),由上图可知,每个块组(Block Group)都由 超级块(Super Block)、块组描述符表(GDT,Group Descriptor Table)、块位图(Block Bitmap)、 inode位图(inode Bitmap)、inode表(inode Table)、数据块(data blocks)六个部分组成。 其中超级块、块组描述符表、块位图、inode位图、inode表这几部分存储该块组的描述信息,数据块(data blocks)则用来存放文件的内容数据。

超级块(Super Block)

超级块记录了整个分区的文件系统信息,超级块记录的信息主要有:
文件系统版本号;
block与inode的总量;
未使用与已使用的inode或block总数量;
block与inode的大小 (block为 1, 2, 4K,inode为128bytes或256bytes);
filesystem的挂载时间、最近一次写入数据的时间、最近一次检验磁盘(fsck)的时间等文件系统的相关信息;
valid bit标志数值,valid bit为0 ,表示此文件系统已被挂载,valid bit为1表示文件系统未被挂载。 Superblock是非常重要的,如果superblock出现错误,那么整个文件系统也就丢失了。所以在每个块组(Block Group)的开头都有一份超级块(Super Block)的相同拷贝, 事实上除了第一个块组内必须含有superblock之外,后面的块组是不一定有superblock的,如果后面块组中含有第一个块组内superblock的备份, 这样就可以在文件系统损坏的时候进行恢复。可以进行 superblock 的救援呢!
一般来说, superblock的大小为1024bytes,可以通过dumpe2fs命令查看superblock的信息!

块组描述符表(GDT,Group Descriptor Table)

块组描述符表中存放着块组描述符,每个块组描述符(GroupDescriptor)存储一个块组的描述信息,描述每个块组中开始与结束的block号码, 以及说明块组的每个组成部分superblock、blockbitmap, inodebitmap, inodetable、data blocks等分别介于哪一个block号码之间。 例如在这个块组中从哪里开始是inode表,从哪里开始是数据块,空闲的inode和数据块还有多少个等等。整个分区分成多少个块组就对应有多少个块组描述符。 和超级块类似,块组描述符表在每个块组的开头也都有一份拷贝,这些信息是非常重要的,一旦超级块意外损坏就会丢失整个分区的文件系统数据, 一旦块组描述符意外损坏就会丢失整个块组的数据,因此它们都有多份拷贝。通常内核只用到第0个块组中的拷贝,当执行e2fsck检查文件系统一致性时, 第0个块组中的超级块和块组描述符表就会拷贝到其它块组,这样当第0个块组的开头意外损坏时就可以用其它拷贝来恢复,从而减少损失。 我们稍后也会用dumpe2fs命令来观察块组描述符信息。

块位图(Block Bitmap)

数据块(DataBlock)用来存储文件的内容数据,一个文件可能占用多个数据块,比如块大小是1024字节,某个文件是2049字节,那么就需要三个数据块来存储, 即使第三个块只存了一个字节也需要占用一个数据块。块位图(Block Bitmap)用来表示块组中数据块的占用与空闲情况。块位图本身占用一个块, 每个bit位代表本块组中的一个块,bit为1表示相应的块已用,bit为0表示相应的块空闲可用。所以在新建文件的时候,就会查询块位图,看看有哪些数据块是空闲的, 将文件数据放入空闲的数据块中,然后将相应的位置1,表示该数据块已经占用。释放文件的时候,就会把相应的数据块位图置0,将数据块恢复到空闲状态。 为什么用df命令统计整个磁盘的已用空间非常快呢?因为只需要查看每个块组的块位图即可,而不需要搜遍整个分区。 相反,用du命令查看一个较大目录的已用空间就非常慢,因为不可避免地要搜遍整个目录的所有文件。
与此相联系的另一个问题是:在格式化一个分区时究竟会划出多少个块组呢?主要的限制在于块位图本身必须只占一个块。 用mke2fs格式化时默认块大小是1024字节,可以用-b参数指定块大小,现在设块大小指定为b字节,那么一个块可以有8b个bit, 这样大小的一个块位图就可以表示8b个块的占用情况,因此一个块组最多可以有8b个块,如果整个分区有s个块,那么就可以有s/(8b)个块组。 格式化时可以用-g参数指定一个块组有多少个块,但是通常不需要手动指定,mke2fs工具会计算出最优的数值。

inode位图(inode Bitmap)

和块位图类似,本身占一个块,其中每个bit表示一个inode是否空闲可用。

inode表(inode Table)

inode表中存储这文件的inode,我们在讨论文件属性的时候,已多次提到过inode,一个文件的内容和属性是分开存储的,文件的内容主要存储在数据块中, 而文件的属性信息主要存储在indode中,除了记录文件的属性信息外,inode中也会记录该文件的内容所在的数据块编号(当然,这也可以认为是文件的一个属性), 这样就可以通过inode找到文件的内容了。
inode记录的文件属性信息包括:
该文件的类型和权限(read/write/excute);
该文件的拥有者与所属用户群组(owner/group);
该文件的容量大小;
该文件建立或状态改变的时间(ctime);文件内容最近一次的读取时间(atime);文件内容最近修改的时间(mtime);
文件的特殊权限,如 SetUID、SETGROUP...等;
文件内容的指向(pointer);
一个文件对应一个inode,一个块组中的所有inode组成了inode表。因此文件系统能够建立的文件数量与inode的数量有关; 系统读取文件时先找到inode,并分析inode所记录的权限与用户是否符合,若符合才能够开始读取block的内容。
inode的数量与大小也是在格式化时就已经固定了,每个inode大小均固定为128bytes (新的ext4与xfs可设定到256bytes); inode表占多少个块在格式化时就要决定并写入块组描述符中,mke2fs格式化工具的默认策略是一个块组有多少个8KB就分配多少个inode。 由于数据块占了整个块组的绝大部分,也可以近似认为数据块有多少个8KB就分配多少个inode,换句话说,如果平均每个文件的大小是8KB, 当分区存满的时候inode表会得到比较充分的利用,数据块也不浪费。如果这个分区存的都是很大的文件(比如电影), 则数据块用完的时候inode会有一些浪费,如果这个分区存的都是很小的文件(比如源代码),则有可能数据块还没用完inode就已经用完了,数据块可能有很大的浪费。 如果用户在格式化时能够对这个分区以后要存储的文件大小做一个预测,也可以用mke2fs的-i参数手动指定每多少个字节分配一个inode。
我们来简单分析一下 EXT2 的 inode / block 与文件大小的关系好了。inode 要记录的数据非常多, 但偏偏又只有 128bytes 而已, 而 inode 记录一个 block 号码要花掉 4byte ,假设我一个文件有 400MB 且每个 block 为 4K 时, 那么至少也要十万个 block 号码的记录!inode 哪有这么多可 记录的信息?为此我们的系统很聪明的将 inode 记录 block 号码的区域定义为 12 个直接,一个间接, 一个双间接与一个三间接记录区。inode结构示意图如下所示: inode结构示意图 inode结构示意图 图.inode 结构示意图
上图最左边为 inode 本身 (128 bytes),里面有 12 个直接指向 block 号码的对照,这 12 笔记录就 能够直接取得 block 号码啦! 至于所谓的间接就是再拿一个 block 来当作记录 block 号码的记录 区,如果文件太大时, 就会使用间接的 block 来记录编号。当中间接只是拿一个 block 来记录额外的号码而已。 同理,如果文件持续长大,那么就会利用所谓的双间接,第一个 block 仅 再指出下一个记录编号的 block 在哪里, 实际记录的在第二个 block 当中。依此类推,三间接就是 利用第三层 block 来记录编号啦!这样子 inode 能够指定多少个 block 呢?我们以较小的 1K block 来说明好了,可以指定的情况如 下: 12 个直接指向: 12*1K=12K
由于是直接指向,所以总共可记录 12 笔记录,因此总额大小为如上所示;
间接: 256*1K=256K
每笔 block 号码的记录会花去 4bytes,因此 1K 的大小能够记录 256 笔记录,因此一个间接可以记录的文件大小如上;
双间接: 256*256*1K=256^2K
第一层 block 会指定 256 个第二层,每个第二层可以指定 256 个号码,因此总额大小如上;
三间接: 256*256*256*1K=256^3K
第一层 block 会指定 256 个第二层,每个第二层可以指定 256 个第三层,每个第三层可以指定 256 个号 码,因此总额大小如上;
总额:将直接、间接、双间接、三间接加总,得到 12 + 256 + 256*256 + 256*256*256 (K) = 16GB
此时我们知道当文件系统将 block 格式化为 1K 大小时,能够容纳的最大文件为 16GB,比较一下 文件系统限制表的结果可发现是一致的!但这个方法不能用在 2K 及 4K block 大小的计算中, 因 为大于 2K 的 block 将会受到 Ext2 文件系统本身的限制,所以计算的结果会不太符合。 Ext4 文件系统的 inode 容量已经可以扩大到 256bytes 了,更大 的 inode 容量,可以纪录更多的文件系统信息,包括新的 ACL 以及 SELinux 类型等, 当然,可以纪录的单一文 件容量达 16TB 且单一文件系统总容量可达 1EB!

数据块data blocks

根据不同的文件类型有以下几种情况:
1.对于普通常规文件,文件的数据存储在数据块中。
2.对于目录,该目录下的所有文件名和目录名存储在数据块中,注意文件名保存在它所在目录的数据块中,除文件名之外,ls-l命令看到的其它信息都保存在该文件的inode中。
注意目录也是一种文件,是一种特殊类型的文件,目录文件中的内容就是该目录下的文件名和目录名。
3.对于符号链接,如果目标路径名较短则直接保存在inode中以便更快地查找,如果目标路径名较长则分配一个数据块来保存。
4.设备文件、FIFO和socket等特殊文件没有数据块,设备文件的主设备号和次设备号保存在inode中。
在 Ext2 文件系统中所支持的 block 大小有 1K, 2K 及4K 三种而已。在格式化时 block 的大小就固定了,且每个 block 都有编号,以方便 inode 的记录。 不过要注意的是,由于 block 大小的差异,会导致该文件系统能够支持的最大磁盘容量与最大 单一文件容量并不相同。 因为 block 大小而产生的 Ext2 文件系统限制如下:
Block 大小 1KB 2KB 4KB
最大单一文件限制 16GB 256GB 2TB
最大文件系统总容量 2TB 8TB 16TB
你需要注意的是,虽然 Ext2 已经能够支持大于 2GB 以上的单一文件容量,不过某些应用程序依然使用旧的限制, 也就是说,某些程序只能够捉到小于 2GB 以下的文件而已,这就跟文件系统无关了!
block特点如下:
原则上,block 的大小与数量在格式化完就不能够再改变了,除非重新格式化;
每个 block 内最多只能够放置一个文件的数据,如果文件大于 block 的大小,则一个文件会占用多个 block 数量;
若文件小于 block ,则该 block 的剩余容量就不能够再被使用了(磁盘空间会浪费)。
由于每个 block 仅能容纳一个文件的数据而已,因此如果你的文件都非常小,但是你的 block 在格式化时却选用最大的 4K 时,可能会产生一些容量的浪费喔! 我们以底下的一个简单例题来算一下空间的浪费吧!
假设你的 Ext2 文件系统使用 4K block ,而该文件系统中有 10000 个小文件,每个文件大小均为 50bytes, 请问 此时你的磁盘浪费多少容量?
由于 Ext2 文件系统中一个 block 仅能容纳一个文件,因此每个 block 会浪费『 4096 - 50 = 4046 (byte)』 , 系统中总共有一万个小文件,所有文件容量为:50 (bytes) x 10000 = 488.3Kbytes,但此时浪费的容量为: 4046 (bytes) x 10000 = 38.6MBytes 。想一想,不到 1MB 的总文件容量却浪费将近 40MB 的容量,且文件越多将造成越多的磁盘容量浪费。
什么情况会产生上述的状况呢?例如 BBS 网站的数据啦!如果 BBS 上面的数据使用的是纯文本文 件来记载每篇留言, 而留言内容如果都写上『如题』时,想一想,是否就会产生很多小文件了呢? 好,既然大的 block 可能会产生较严重的磁盘容量浪费,那么我们是否就将 block 大小订为 1K 即 可? 这也不妥,因为如果 block 较小的话,那么大型文件将会占用数量更多的 block ,而 inode 也 要记录更多的 block 号码,此时将可能导致文件系统不良的读写效能。
所以在您进行文件系统的格式化之前,请先想好该文件系统预计使用的情况。
事实上,现在的磁盘容量都太大了!所以,大概大家都只会选择 4K 的 block 大小!

三.用dumpe2fs命令查询Ext家族文件系统信息

回到顶部
dumpe2fs命令只能用来查询Ext家族文件系统的信息,最新的centos默认安装的XFS文件系统是无法使用的。 dumpe2fs命令常用格式如下
            [root@initroot ~]# dumpe2fs [-bh] 装置文件名
            
选项与参数:
-b :列出保留为坏轨的部分(一般用不到)
-h :仅列出superblock的数据,不会列出其他的区段内容!
我们先用blkid命令显示出目前系统中被格式化的文件系统设备文件,然后用和lsblk命令显示系统中各个文件系统的挂载点: [root@initroot ~]# blkid /dev/vda1: LABEL="myboot" UUID="ce4dbf1b-2b3d-4973-8234-73768e8fd659" TYPE="xfs" /dev/vda2: LABEL="myroot" UUID="21ad8b9a-aaad-443c-b732-4e2522e95e23" TYPE="xfs" /dev/vda3: UUID="12y99K-bv2A-y7RY-jhEW-rIWf-PcH5-SaiApN" TYPE="LVM2_member" /dev/vda5: UUID="e20d65d9-20d4-472f-9f91-cdcfb30219d6" TYPE="ext4"
            root@initroot:~$ blkid
            /dev/sda1: UUID="2626690a-22a9-4ec7-9c92-568102efebe2" TYPE="ext4" PARTUUID="2733adeb-01"
            peter@peter-VirtualBox:~$ lsblk
            NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
            sda      8:0    0   70G  0 disk 
            └─sda1   8:1    0   70G  0 part /
            sr0     11:0    1 56.8M  0 rom  
            
通过上面我们发现当前系统中的磁盘只有一个分区,该分区已经被格式化为ext4文件系统,设备文件为/dev/sda1, 该分区文件系统挂载点为/ 我们现在用dumpe2fs命令查看给文件系统信息:
              root@initroot:~$ sudo dumpe2fs /dev/sda1 
[sudo] password for peter:       
dumpe2fs 1.44.1 (24-Mar-2018)
Filesystem volume name:   <none>     # 文件系统的名称(不一定会有)
Last mounted on:          /             # 上一次挂载的目录位置
Filesystem UUID:          2626690a-22a9-4ec7-9c92-568102efebe2   #UUID文件系统设备的UUID
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Filesystem flags:         signed_directory_hash 
Default mount options:    user_xattr acl   # 默认在挂载时会主动加上的挂载参数
Filesystem state:         clean            # 这块文件系统的状态为何,clean表示没问题
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              4587520         # inode的总数
Block count:              18349568        # block的总数
Reserved block count:     917478          # 保留的block总数
Free blocks:              12699869        # 空闲的block数量
Free inodes:              3934373         # 空闲的inode
First block:              0
Block size:               4096            # 单个block的容量大小
Fragment size:            4096
Group descriptor size:    64
Reserved GDT blocks:      1024
Blocks per group:         32768
Fragments per group:      32768
Inodes per group:         8192
Inode blocks per group:   512
Flex block group size:    16
Filesystem created:       Mon Nov 18 16:56:17 2019
Last mount time:          Fri Jan 10 09:14:48 2020
Last write time:          Fri Jan 10 09:14:46 2020
Mount count:              104
Maximum mount count:      -1
Last checked:             Mon Nov 18 16:56:17 2019
Check interval:           0 (<none>)
Lifetime writes:          35 GB
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:	              256                     # inode的容量大小!已经是256了!
Required extra isize:     32
Desired extra isize:      32
Journal inode:            8
First orphan inode:       3286218
Default directory hash:   half_md4
Directory Hash Seed:      9d19362d-d47a-48e2-a75c-beb1651488df
Journal backup:           inode blocks
Checksum type:            crc32c
Checksum:                 0xda8e8756
Journal features:         journal_incompat_revoke journal_64bit journal_checksum_v3
Journal size:             512M                      # Journal日志式数据的可供纪录总容量
Journal length:           131072
Journal sequence:         0x0002d4bc
Journal start:            1
Journal checksum type:    crc32c
Journal checksum:         0x9f485494

Group 0: (Blocks 0-32767) csum 0x2056 [ITABLE_ZEROED]  # 第一个block group
  Primary superblock at 0, Group descriptors at 1-9    # 主要superblock!
  Reserved GDT blocks at 10-1033
  Block bitmap at 1034 (+1034), csum 0x50aedd5d
  Inode bitmap at 1050 (+1050), csum 0x5d2be9d0
  Inode table at 1066-1577 (+1066)                     # inode table!
  3034 free blocks, 8175 free inodes, 2 directories, 8173 unused inodes
  Free blocks: 21546-24575, 32764-32767                # 空闲的blocks
  Free inodes: 13, 19-8192                             # 空闲的inodes
Group 1: (Blocks 32768-65535) csum 0x5ae2 [INODE_UNINIT, ITABLE_ZEROED]  # 第二块后续为更多其他的 block group 喔!
  Backup superblock at 32768, Group descriptors at 32769-32777
  Reserved GDT blocks at 32778-33801
  Block bitmap at 1035 (bg #0 + 1035), csum 0x106c8024
  Inode bitmap at 1051 (bg #0 + 1051), csum 0x00000000
  Inode table at 1578-2089 (bg #0 + 1578)
  694 free blocks, 8192 free inodes, 0 directories, 8192 unused inodes
  Free blocks: 33803-33816, 33818-34047, 34070-34079, 34102-34143, 34166-34175, 34199-34207, 34229-34271, 34293-34431, 34450-34463, 34484-34495, 34518-34527, 34548-34559, 34583-34591, 34611-34655, 34675-34687, 34714-34751, 34772-34815
  Free inodes: 8193-16384
Group 2: (Blocks 65536-98303) csum 0xa1ac [INODE_UNINIT, ITABLE_ZEROED]
  Block bitmap at 1036 (bg #0 + 1036), csum 0x3f85d120
  Inode bitmap at 1052 (bg #0 + 1052), csum 0x00000000
  Inode table at 2090-2601 (bg #0 + 2090)
  0 free blocks, 8192 free inodes, 0 directories, 8192 unused inodes
  Free blocks: 
  Free inodes: 16385-24576

  ...省略...
              
dumpe2fs命令输出的信息实在太多了,我们截取部分需要重点关注的信息。我们对主要的字段都加了注释。 前半部是supberblock 的内容,包括标签名称(Label)以及 inode/block 的相关信息。后面则是每个 block group 的信息了!
/dev/vda5 规划的 block 为 4K, 第一个 block 号码为 0 号,且 block group 内的所有信息都以block 的号码来表示的。 然后在 superblock 中还有谈到目前这个文件系统的可用 block 与 inode 数量!
至于 block group 的内容我们单纯看 Group0 信息好了。从上表中我们可以发现: Group0 所占用的 block 号码由 0 到 32767 号,superblock 则在第 0 号的 block 区块内! 文件系统描述说明在第 1 号 block 中; block bitmap 与 inode bitmap 则在 129 及 145 的 block 号码上。 至于 inode table 分布于 161-672 的 block 号码中! 由于(1)一个 inode 占用 256 bytes , (2)总共有 672 - 161 + 1(161 本身) = 512 个 block 花在 inode table 上, (3)每个 block 的大小为 4096 bytes(4K)。由这些数据可以算出 inode 的数量共有 512 * 4096 / 256 = 8192 个 inode!
这个 Group0 目前可用的 block 有 28521 个,可用的 inode 有 8181 个; 剩余的 inode 号码为 12 号到 8192 号。
我们通过几个例子来加深对文件系统概念的理解,在在home目录下ls -l:
数据的不一致 (Inconsistent)状态
在一般正常的情况下,上述的新增动作当然可以顺利的完成。但是如果有个万一怎么办? 例如你的文件在写入文件系统时,因为不知名原因导致系统中断(例如突然的停电啊、 系统核心发生错误啊~等等的怪事发生时), 所以写入的数据仅有 inode table 及 data block 而已, 最后一个同步更新中介数据的步骤并没有做完,此时就会发生 metadata 的内容与实际数据存放区产生不一致(Inconsistent) 的情况了。
既然有不一致当然就得要克服!在早期的 Ext2 文件系统中,如果发生这个问题, 那么系统在重新启动的时候,就会藉由 Superblock 当中记录的 valid bit (是否有挂载) 与 filesystem state (clean 与否) 等状态来判断是否强制进行数据一致性的检查! 若有需要检查时则以 e2fsck 这支程序来进行的。
不过,这样的检查真的是很费时~因为要针对 metadata 区域与实际数据存放区来进行比对, 呵呵~ 得要搜寻整个 filesystem 呢~如果你的文件系统有 100GB 以上,而且里面的文件数量又多时, 哇! 系统真忙碌~而且在对 Internet 提供服务的服务器主机上面, 这样的检查真的会造成主机复原时间的拉长~真是麻烦~这也就造成后来所谓日志式文件系统的兴起了。
日志式文件系统 (Journaling filesystem)
为了避免上述提到的文件系统不一致的情况发生,因此我们的前辈们想到一个方式, 如果在我们的filesystem 当中规划出一个区块,该区块专门在记录写入或修订文件时的步骤, 那不就可以简化一致性检查的步骤了?也就是说:
1. 预备:当系统要写入一个文件时,会先在日志记录区块中纪录某个文件准备要写入的信息;
2. 实际写入:开始写入文件的权限与数据;开始更新 metadata 的数据;
3. 结束:完成数据与 metadata 的更新后,在日志记录区块当中完成该文件的纪录。
在这样的程序当中,万一数据的纪录过程当中发生了问题,那么我们的系统只要去检查日志记录区块, 就可以知道哪个文件发生了问题,针对该问题来做一致性的检查即可,而不必针对整块 filesystem 去检查, 这样就可以达到快速修复 filesystem 的能力了!这就是日志式文件最基础的功能啰~
那么我们的 ext2 可达到这样的功能吗?当然可以啊! 就透过 ext3/ext4 即可! ext3/ext4 是 ext2 的升级版本, 并且可向下兼容 ext2 版本呢! 所以啰,目前我们才建议大家,可以直接使用 ext4 这个filesystem 啊! 如果你还记得 dumpe2fs 输出的讯息,可以发现 superblock 里面含有底下这样的信息:
Journal inode: 8
Journal backup: inode blocks
Journal features: (none)
Journal size: 32M
Journal length: 8192
Journal sequence: 0x00000001
看到了吧!透过 inode 8 号记录 journal 区块的 block 指向,而且具有 32MB 的容量在处理日志呢!
这样对于所谓的日志式文件系统有没有比较有概念一点呢?^_^。

filesystem大小与磁盘读取性能的关系

回到顶部
当一个文件系统规划的很大时,例如100GB这么大, 由于磁盘上面的数据总是来来去去的,所以,整个文件系统上面的文件通常无法连续写在一起(block号码不会连续的意思), 而是填入式的将数据填入没有被使用的 block 当中。如果文件写入的 block 真的分的很散, 此时就会有所谓的文件数据离散的问题发生了。
如前所述,虽然我们的 ext2 在 inode 处已经将该文件所记录的 block 号码都记上了, 所以资料可以一次性读取,但是如果文件真的太过离散,确实还是会发生读取效率低落的问题。 因为磁盘读取头还是得要在整个文件系统中来来去去的频繁读取! 果真如此,那么可以将整个 filesystme 内的数据全部复制出来,将该 filesystem 重新格式化, 再将数据给他复制回去即可解决这个问题。
此外,如果 filesystem 真的太大了,那么当一个文件分别记录在这个文件系统的最前面与最后面的block 号码中, 此时会造成磁盘的机械手臂移动幅度过大,也会造成数据读取效能的低落。 而且读取头在搜寻整个 filesystem 时, 也会花费比较多的时间去搜寻! 因此, partition 的规划并不是越大越好, 而是真的要针对您的主机用途来进行规划才行!

initroot编辑整理,转载请注明www.initroot.com

100次点赞 100次阅读