深入剖析Linux文件系统之ext2路径名查找(超详细~)
本文以ext2文件系统为例来剖析一个真实的文件系统如何查找文件,这对于深入理解文件系统至关重要。
1.准备文件系统镜像
所用工具:dd、mkfs.ext2、hexdump、dumpe2fs、mount等工具
1)制作100k大小镜像文件
2)格式化为ext2文件系统格式
3)查看文件系统信息
这实际是是读取文件系统的超级块和块组描述符信息。我们可以看的创建的文件系统的总体信息:
Filesystem magic number:0xEF53 表示为ext2文件系统Inode count: 16 表示文件系统inode个数为16Block count: 100 表示文件系统块个数为100 Free blocks: 79 表示文件系统空闲块个数为79Free inodes: 5 表示文件系统空闲inode个数为5 First block: 1 第一个数据块编号为1(编号0保留为引导块) Block size: 1024 文件系统块大小为1kBlocks per group: 8192 每个块组8192个块Inodes per group: 16 每个块组个inode Inode blocks per group: 2 每个块组2个inode块 First inode: 11 分配的第一个inode号为11(除根inode外,根inode号为2)Inode size: 128 inode大小为128字节块组的信息(这里只有一个块组) 1 - 99号 超级块块编号为 1 块组描述符块编号为 2 块位图块编号为 3 inode位图块编号为 4
inode表位于5和6块
79 个可用 块,5 个可用inode,2 个目录 (一个为根目录一个为lost+found,存放坏块) 可用块数:21-99 可用inode数:12-16
【文章福利】小编推荐自己的Linux内核技术交流群:【891587639】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!(含视频教程、电子书、实战项目及代码)


4)挂载文件系统并创建文件
可以发现有三个目录:
实际上是根目录的数据块的内容(包含各个目录项)。
下面我们来创建一个目录,目录下创建文件:
现在目录树是这样的:
后面我们会通过解析文件系统镜像来观察如何查找 /dir/test.txt 文件的
现在关注一下相关的索引节点:
可以发现 /dir目录下:当前工作目录下索引节点为12(dir目录的),上一级目录的索引节点为2(根目录),test.txt文件的所有节点为13。记住这几个索引节点后面我们会通过解析文件系统镜像来获得。
2.解析文件系统镜像
1)dump文件系统镜像
根据之前dumpe2fs的信息我们知道:
镜像文件中(均为16进制显示) 00000000 开始的1k大小 保留的引导块 块1 00000400 开始的1k大小 保存磁盘的超级块 (dumpe2fs的部分信息从这里获得) 块2 00000800 开始的1k大小 保存块组描述符 (dumpe2fs的部分信息从这里获得) 块3 00000c00 开始的1k大小 保存块位图 块4 00001000 开始的1k大小 保存 Inode 位图 块5 块6 00001400 开始的2k大小 保存 Inode表 剩下的为数据块
磁盘中的文件系统对象结构在内核如下文件定义:
大家可以对照磁盘镜像文件和磁盘数据结构定义来解析出文件系统的超级块和块组描述符信息(可以发现和dumpe2fs工具显示的是一致的,例如镜像文件00000400 处四字节为10 00 00 00 是小端存储,所以为0x00000010=16);
3.路径名查找
下面开始我们的重头戏:查找文件系统中的 /dir/test.txt 文件。
我们知道,使用文件系统给我最直观也是最大的好处是:用户可以通过一个路径名来访问文件,那么一个文件系统究竟如何来找到我们所需要的文件呢?下面我们详细来看ext2文件系统如何查找指定的文件的?(实际的内核中路径名查找比较复杂,考虑很多情况,如dentry cache查找、解析软链接文件、上级目录、挂载点等,当然如果目录分量是挂载点就会步进到相应文件系统的根目录,后面文件系统挂载专题会讲解,这里以简单的路径解析来让大家有个深刻的认识)。
1)查找根目录
万事开头难,对于访问一个目录上挂载的文件系统,内核路径名查找会判断并找到挂载的文件系统的根目录,这个过程在文件系统挂载的时候,会从磁盘上读取并在内存构建超级块实例,然后进行的最重要的一步是读取文件系统的根inode:
简述ext2通过inode号找到并读取磁盘inode核心算法:
根据inode号计算出所在的块组block_group
根据inode号计算出块组中的inode表中的字节偏移offset
根据inode号计算出磁盘inode在文件系统中的块号block
根据块号block 通过sb_bread读取缓冲区块到内存
根据inode表中的字节偏移offset 计算出 磁盘inode在块中偏移
通过读取的缓冲区和磁盘inode在块中偏移 最终返回磁盘inode结构
我们已知:
每个块组inode个数:EXT2_INODES_PER_GROUP(sb) = 16 磁盘inode大小:EXT2_INODE_SIZE(sb) = 128 块大小的bit表示:EXT2_BLOCK_SIZE_BITS(sb) = 10
所以计算根inode块号:
所以:根inode所在的镜像文件中偏移为:5 * 0x400 + 0x80 = 0x1400 + 0x80 = 0x1480
对照ext2文件系统磁盘inode结构,可知i_block为磁盘inode结构的偏移40B处,内容即为0x07(ext2通过i_block来查找文件在磁盘中的位置)。
于是我们知道,根目录数据块的块号 为0x7(镜像中字节偏移为 0x400 * 7= 1c00),这个数据块中保存的是根目录中包含的所有目录和文件的目录项(我们知道这里为"."、".."、"dir"、"lost+found"四个目录项)。
根据目录项ext2_dir_entry_2 结构我们可以查询到文件名为dir的目录项,从而获取dir目录的inode号,为0x0c(和我们之前通ls -lai显示的dir目录inode号12是一致)。
2)查找dir目录
和上面查询根inode一样的原理,计算过程如下:
所以:dir目录inode所在的镜像文件中字节偏移为:6 * 0x400 + 0x180 = 0x1800 + 0x180 = 0x1980
对照ext2文件系统磁盘inode结构,可知i_block为磁盘inode结构的偏移40B处,内容即为0x63。
于是我们知道,dir目录数据块的块号 为0x63(偏移为 0x400 * 0x63= 0x18c00),这个数据块中保存的是dir目录中包含的所有目录和文件的目录项(我们知道这里为"."、".."、"test.txt"三个目录项)。
对照目录项ext2_dir_entry_2 结构,查找文件名为test.txt的inode号,即为0x0d(和我们之前通ls -lai显示的dir目录inode号13是一致)。
于是我们知道,test.txt文件的inode号为0x0d(13)。
3)查找test.txt文件
和上面查询根inode一样的原理,计算过程如下:
所以:test.txt文件inode所在的镜像文件中偏移为:= 6 * 0x400 + 0x200 = 0x1800 + 0x200 = 0x1a00
对照ext2文件系统磁盘inode结构,可知i_block为磁盘inode结构的偏移40B处,内容即为0x15。
于是我们知道,test.txt文件数据块的块号 为0x15(偏移为0x15 * 0x400 = 0x5400)。
最终可以看到文件数据为"hello"。
4)查找过程图解
以下为 dir/test.txt查找过程:
已知根目录inode号(ext2为2) -> 查找根目录磁盘inode(文件系统挂载时查找) -> 查找根目录的数据块 -> 查找dir目录的目录项找到其inode号 (为12) -> 查找dir目录的磁盘inode -> 查找dir目录的数据块 -> 查找test.txt文件的inode号(为13) -> 查找test.txt文件的磁盘inode -> 查找test.txt文件的数据块
下面为查找图解:

4.总结
对于ext2文件系统,路径名查找中,实际上是解析路径名的各个分量,查找每个分量的目录项,然后通过目录项找到inode号,通过inode号找到对应的磁盘inode,然后通过磁盘inode获得目录/文件的数据块, 最终查找到对应目录/文件的磁盘inode,而磁盘inode的i_block中保存着文件的逻辑块号和磁盘的逻辑块号映射关系,读写文件时就可以访问到整个文件。
