ext2文件系统读写过程解析

每个文件(不管是一般文件还是目录文件)都会占用一个 inode , 且可依据文件内容的大小来分配多个 block 给该文件使用。
目录
当我们在 Linux 下的 ext2 文件系统创建一个目录时, ext2 会分配一个 inode 与至少一块 block 给该目录。其中,inode 记录该目录的相关权限与属性,并可记录分配到的那块 block 号码; 而 block 则是记录在这个目录下的文件名与该文件名占用的 inode 号码数据。也就是说目录所占用的 block 内容在记录如下的信息:
这里写图片描述
如果想要实际观察 root 家目录内的文件所占用的 inode 号码时,可以使用 ls -i 这个选项来处理:

[root@www ~]# ls -li
total 92
654683 -rw------- 1 root root  1474 Sep  4 18:27 anaconda-ks.cfg
648322 -rw-r--r-- 1 root root 42304 Sep  4 18:26 install.log
648323 -rw-r--r-- 1 root root  5661 Sep  4 18:25 install.log.syslog

由于每个人所使用的计算机并不相同,系统安装时选择的项目与 partition 都不一样,因此你的环境不可能与我的 inode 号码一模一样!由这个目录的 block 结果我们现在就能够知道, 当你使用『 ll / 』时,出现的目录几乎都是 1024 的倍数,为什么呢?因为每个 block 的数量都是 1K, 2K, 4K 嘛! 看一下鸟哥的环境:

[root@www ~]# ll -d / /bin /boot /proc /lost+found /sbin
drwxr-xr-x 23 root root  4096 Sep 22 12:09 /           <==一个 4K block
drwxr-xr-x  2 root root  4096 Sep 24 00:07 /bin        <==一个 4K block
drwxr-xr-x  4 root root  1024 Sep  4 18:06 /boot       <==一个 1K block
drwx------  2 root root 16384 Sep  5 01:49 /lost+found <==四个 4K block
dr-xr-xr-x 96 root root     0 Sep 22 20:07 /proc       <==此目录不占硬盘空间
drwxr-xr-x  2 root root 12288 Sep  5 12:33 /sbin       <==三个 4K block

由于鸟哥的根目录 /dev/hdc2 使用的 block 大小为 4K ,因此每个目录几乎都是 4K 的倍数。 其中由于 /sbin 的内容比较复杂因此占用了 3 个 block ,此外,鸟哥的系统中 /boot 为独立的 partition , 该 partition 的 block 为 1K 而已,因此该目录就仅占用 1024 bytes 的大小!至于奇怪的 /proc 我们在第六章就讲过该目录不占硬盘容量, 所以耗用的 block 就是 0 !

tips:由上面的结果我们知道目录并不只会占用一个 block 而已,也就是说: 在目录底下的文件数如果太多而导致一个 block 无法容纳的下所有的档名与 inode 对照表时,Linux 会给予该目录多一个 block 来继续记录相关的数据;

文件
当我们在 Linux 下的 ext2 创建一个一般文件时, ext2 会分配一个 inode 与相对于该文件大小的 block 数量给该文件。例如:假设我的一个 block 为 4 Kbytes ,而我要创建一个 100 KBytes 的文件,那么 linux 将分配一个 inode 与 25 个 block 来储存该文件! 但同时请注意,由于 inode 仅有 12 个直接指向,因此还要多一个 block 来作为区块号码的记录!

目录树读取
好了,经过上面的说明你也应该要很清楚的知道 inode 本身并不记录文件名,文件名的记录是在目录的 block 当中。 因此在第六章文件与目录的权限说明中, 我们才会提到『新增/删除/更名文件名与目录的 w 权限有关』的特色!那么因为文件名是记录在目录的 block 当中, 因此当我们要读取某个文件时,就务必会经过目录的 inode 与 block ,然后才能够找到那个待读取文件的 inode 号码, 最终才会读到正确的文件的 block 内的数据。

由于目录树是由根目录开始读起,因此系统透过挂载的信息可以找到挂载点的 inode 号码(通常一个 filesystem 的最顶层 inode 号码会由 2 号开始!),此时就能够得到根目录的 inode 内容,并依据该 inode 读取根目录的 block 内的文件名数据,再一层一层的往下读到正确的档名。

举例来说,如果我想要读取 /etc/passwd 这个文件时,系统是如何读取的呢?

[root@www ~]# ll -di / /etc /etc/passwd
      2 drwxr-xr-x  23 root root  4096 Sep 22 12:09 /
1912545 drwxr-xr-x 105 root root 12288 Oct 14 04:02 /etc
1914888 -rw-r--r--   1 root root  1945 Sep 29 02:21 /etc/passwd

在鸟哥的系统上面与 /etc/passwd 有关的目录与文件数据如上表所示,该文件的读取流程为(假设读取者身份为 vbird 这个一般身份使用者):
1、/ 的 inode:
透过挂载点的信息找到 /dev/hdc2 的 inode 号码为 2 的根目录 inode,且 inode 规范的权限让我们可以读取该 block 的内容(有 r 与 x) ;
2、/ 的 block:
经过上个步骤取得 block 的号码,并找到该内容有 etc/ 目录的 inode 号码 (1912545);
3、etc/ 的 inode:
读取 1912545 号 inode 得知 vbird 具有 r 与 x 的权限,因此可以读取 etc/ 的 block 内容;
4、etc/ 的 block:
经过上个步骤取得 block 号码,并找到该内容有 passwd 文件的 inode 号码 (1914888);
5、passwd 的 inode:
读取 1914888 号 inode 得知 vbird 具有 r 的权限,因此可以读取 passwd 的 block 内容;
6、passwd 的 block:

EXT2/EXT3文件的存取与日志式文件系统的功能
上一小节谈到的仅是读取而已,那么如果是新建一个文件或目录时,我们的 Ext2 是如何处理的呢? 这个时候就得要 block bitmap 及 inode bitmap 的帮忙了!假设我们想要新增一个文件,此时文件系统的行为是:

1、先确定用户对于欲新增文件的目录是否具有 w 与 x 的权限,若有的话才能新增;
2、根据 inode bitmap 找到没有使用的 inode 号码,并将新文件的权限/属性写入;
3、根据 block bitmap 找到没有使用中的 block 号码,并将实际的数据写入 block 中,且升级 inode 的 block 指向数据;
4、将刚刚写入的 inode 与 block 数据同步升级 inode bitmap 与 block bitmap,并升级 superblock 的内容。

一般来说,我们将 inode table 与 data block 称为数据存放区域,至于其他例如 superblock、 block bitmap 与 inode bitmap 等区段就被称为 metadata (元数据) ,因为 superblock, inode bitmap 及 block bitmap 的数据是经常变动的,每次新增、移除、编辑时都可能会影响到这三个部分的数据,因此被称为元数据。

数据的不一致 (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 即可! ext3 是 ext2 的升级版本,并且可向下兼容 ext2 版本!目前我们建议大家可以直接使用 ext3 这个 filesystem! 如果你还记得 dumpe2fs 输出的信息,可以发现 superblock 里面含有底下这样的信息:

Journal inode:            8 
Journal backup:           inode blocks
Journal size:             128M

看到了吧!透过 inode 8 号记录 journal 区块的 block 指向,而且具有 128MB 的容量在处理日志!如果想要知道为什么 Ext3 文件系统会更适用于目前的 Linux 系统, 我们可以参考 Red Hat 公司中,首席核心开发者 Michael K. Johnson 的话:
『为什么你想要从ext2转换到ext3呢?有四个主要的理由:可利用性、数据完整性、速度及易于转换』 『可利用性』,他指出,这意味着从系统中止到快速重新复原而不是持续的让e2fsck运行长时间的修复。ext3 的日志式条件可以避免数据毁损的可能。他也指出: 『除了写入若干数据超过一次时,ext3往往会较快于ext2,因为ext3的日志使硬盘读取头的移动能更有效的进行』 然而或许决定的因素还是在Johnson先生的第四个理由中。
『它是可以轻易的从ext2变更到ext3来获得一个强而有力的日志式文件系统而不需要重新做格式化』。『那是正确的,为了体验一下 ext3 的好处是不需要去做一种长时间的,冗长乏味的且易于产生错误的备份工作及重新格式化的动作』。

Linux 文件系统的运行:
我们现在知道了目录树与文件系统的关系了,但是由第零章的内容我们也知道, 所有的数据都得要加载到内存后 CPU 才能够对该数据进行处理。想一想,如果你常常编辑一个好大的文件, 在编辑的过程中又频繁的要系统来写入到磁盘中,由于磁盘写入的速度要比内存慢很多, 因此你会常常耗在等待硬盘的写入/读取上。真没效率!

为了解决这个效率的问题,因此我们的 Linux 使用的方式是透过一个称为异步处理 (asynchronously) 的方式。所谓的异步处理是这样的:

当系统加载一个文件到内存后,如果该文件没有被更动过,则在内存区段的文件数据会被配置为干净(clean)的。 但如果内存中的文件数据被更改过了(例如你用 nano 去编辑过这个文件),此时该内存中的数据会被配置为脏的 (Dirty)。此时所有的动作都还在内存中运行,并没有写入到磁盘中! 系统会不定时的将内存中配置为『Dirty』的数据写回磁盘,以保持磁盘与内存数据的一致性。 你也可以利用第五章谈到的 sync命令来手动强迫写入磁盘。

我们知道内存的速度要比硬盘快的多,因此如果能够将常用的文件放置到内存当中,这不就会添加系统性能吗? 没错!是有这样的想法!因此我们 Linux 系统上面文件系统与内存有非常大的关系:

系统会将常用的文件数据放置到主存储器的缓冲区,以加速文件系统的读/写;
承上,因此 Linux 的物理内存最后都会被用光!这是正常的情况!可加速系统效能;
你可以手动使用 sync 来强迫内存中配置为 Dirty 的文件回写到磁盘中;
若正常关机时,关机命令会主动呼叫 sync 来将内存的数据回写入磁盘内;
但若不正常关机(如跳电、死机或其他不明原因),由于数据尚未回写到磁盘内, 因此重新启动后可能会花很多时间在进行磁盘检验,甚至可能导致文件系统的损毁(非磁盘损毁)。

猜你喜欢

转载自blog.csdn.net/gongjiwei/article/details/82154703
今日推荐