实验机器:虚拟机 + red hat 9
 
先看 ext2/ext3 文件系统 基本结构
由于机器面对的是 字节流,因此必须对 字节流 进行结构化定义,文件系统亦如此。
下面介绍 ext2/ext3 文件系统的结构。
 
ext2/ext3 结构图:

ext2 / ext3 结构 - lizeaaaa - Hello World
 
 
 
 
Part 1. Super block,1024 B itself  / take up 1 block size
 
The superblock contains all the information about the configuration of the filesystem. The information in
the superblock contains fields such as the total number of inodes and blocks in the filesystem and how
many are free, how many inodes and blocks are in each block group, when the filesystem was mounted
(and if it was cleanly unmounted), when it was modified, what version of the filesystem it is and which
OS created it.
The primary copy of the superblock is stored at an offset of 1024 bytes from the start of the device, and it
is essential to mounting the filesystem.
 Since it is so important, backup copies of the superblock are
stored in block groups throughout the filesystem.

The first version of ext2 (revision 0) stores a copy at the start of every block group, along with backups
of the group descriptor block(s). Because this can consume a considerable amount of space for large
filesystems, later revisions can optionally reduce the number of backup copies by only putting backups in
specific groups
 (this is the sparse superblock feature). The groups chosen are 0, 1 and powers of 3, 5 and
7.

Revision 1 and higher of the filesystem also store extra fields, such as a volume name, a unique
identification number, the inode size, and space for optional filesystem features to store configuration
info.
All fields in the superblock (as in all other ext2 structures) are stored on the disc in little endian format,
so a filesystem is portable between machines without having to know what machine it was created on.
 

struct ext3_super_block {


/*00*/
__u32 s_inodes_count;/* inodes 计数 */
__u32 s_blocks_count;/* blocks 计数 */
__u32 s_r_blocks_count;/* 保留的 blocks 计数 */
__u32 s_free_blocks_count;/* 空闲的 blocks 计数 */

/*10*/
__u32 s_free_inodes_count;/* 空闲的 inodes 计数 */
__u32 s_first_data_block;/* 第一个数据 block */
__u32 s_log_block_size;/* block 的大小 */
__s32 s_log_frag_size;/* 可以忽略 */

/*20*/ 
__u32 s_blocks_per_group;/* 每 block group 的 block 数量 */
__u32 s_frags_per_group;/* 可以忽略 */
__u32 s_inodes_per_group;/* 每 block group 的 inode 数量 */
__u32 s_mtime;/* Mount time */

/*30*/ 
__u32 s_wtime;/* Write time */
__u16 s_mnt_count;/* Mount count */
__s16 s_max_mnt_count;/* Maximal mount count */
__u16 s_magic;/* Magic 签名 */
__u16 s_state;/* File system state */
__u16 s_errors;/* Behaviour when detecting errors */
__u16 s_minor_rev_level;/* minor revision level */

/*40*/ 
__u32 s_lastcheck;/* time of last check */
__u32 s_checkinterval;/* max. time between checks */
__u32 s_creator_os;/* 可以忽略 */
__u32 s_rev_level;/* Revision level */

/*50*/ 
__u16 s_def_resuid;/* Default uid for reserved blocks */
__u16 s_def_resgid;/* Default gid for reserved blocks */
__u32 s_first_ino;/* First non-reserved inode */
__u16 s_inode_size;/* size of inode structure */
__u16 s_block_group_nr;/* block group # of this superblock */
__u32 s_feature_compat;/* compatible feature set */

/*60*/ 
__u32 s_feature_incompat;/* incompatible feature set */
__u32 s_feature_ro_compat;/* readonly-compatible feature set */

/*68*/ 
__u8  s_uuid[16];          /* 128-bit uuid for volume */

/*78*/ 
char  s_volume_name[16];/* volume name */

/*88*/ 
char  s_last_mounted[64];/* directory where last mounted */

/*C8*/ 
__u32 s_algorithm_usage_bitmap;/* 可以忽略 */
__u8 s_prealloc_blocks;/* 可以忽略 */
__u8 s_prealloc_dir_blocks;/* 可以忽略 */
__u16 s_padding1;/* 可以忽略 */

/*D0*/ 
__u8  s_journal_uuid[16];/* uuid of journal superblock */

/*E0*/ 
__u32 s_journal_inum;/* 日志文件的 inode 号数 */
__u32 s_journal_dev;/* 日志文件的设备号 */
__u32 s_last_orphan;/* start of list of inodes to delete */

/*EC*/ 
__u32 s_reserved[197];/* 可以忽略 */
};
 
0.
无论分区的 block size 是多大,Super block 总是始于 存储装置的 偏移1024 B 处!
同时, Super block 使用小端存储!
这2点保证了可移植性!
 
1.
block 的大小 = 1 << (s_log_block_size+10),单位Byte
 3. 得到,s_log_block_size = 0, 1 << 10 = 2^10 = 1024
 
2.
注意 s_magic,这个位在 ext2 和 ext3 是相同的,
这个位 类似于 TCP/IP 协议中多路复用的 幻数,这说明了ext2 和 ext3 的兼容性很好
 
3.
dumpe2fs 这个命令本身就是用来查看 super block 的
我们先看一下这个分区的 block 大小:
# dumpe2fs /dev/sda1
.......
Inode count:              24096
Block count:              96358
.......
First block:              1
Block size:               1024
.......
 
简单截取 4 个信息:
Inode count: 24096    代表 inode 总数;
Block count: 96358    代表 block 总数;
Block size: 1024    代表分区的 block 大小;
First block: 1    代表 /dev/sda1 这个设备是从第一个 block 开始写数据的,
 
4. 
查看 硬盘内容,可以使用 dd 命令,
这里说明 /dev/sda1 的第 0 个 block 没有使用:
dd if=/dev/sda1 bs=1024 count=1 skip=0 | xxd | less 验证,将会看到全为 0
skip=0 强调是第 0 个,不跳
 
由于 1 个 Super Block 是 1024B,我们执行下面的命令,看看这 1024B 的内容:
# dd if=/dev/sda1 bs=1024 count=1 skip=1 | xxd | less
0000000: 205e 0000 6678 0100 d112 0000 aa47 0100   ^..fx.......G..
0000010: f65d 0000 0100 0000 0000 0000 0000 0000  .]..............
0000020: 0020 0000 0020 0000 d807 0000 6685 344f  . ... ......f.4O
0000030: 6685 344f 2800 ffff 53ef 0100 0100 0000  f.4O(...S.......
0000040: f92a 254f 0000 0000 0000 0000 0100 0000  .*%O............
0000050: 0000 0000 0b00 0000 8000 0000 0400 0000  ................
0000060: 0600 0000 0100 0000 981f fa3c b538 4a2b  ...........<.8J+
0000070: ba5e 9c83 ceeb 34fe 2f62 6f6f 7400 0000  .^....4./boot...
0000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s ...
00000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000e0: 0800 0000 0000 0000 0000 0000 125f b5b3  ............._..
00000f0: 29d5 46c5 99a4 7da6 c8af 233a 0200 0000  ).F...}...#:....
0000100: 0000 0000 0000 0000 f92a 254f 0000 0000  .........*%O....
0000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s ...
00003f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
可以看出,根据 ext3_super_block 结构体,以及英文介绍得知 superblock 格式是 小端 的,
第一个便是 u32,205e 0000,即 5e20,十进制 24096,inode 总数
第二个也是 u32,6678 0100,即 17866,十进制 96358,block 总数
这 2 个数字与 dumpe2fs /dev/sda1 相符
 
5.
拷贝备份
英文介绍中说,Super Block 的备份在 ext2 中,是在所有的 group block 中的 Super block 中备份,而后的版本只
0,1,3,5,7, 以及3 5 7的指数的 group block 号中的 Super Block 做拷贝,如 9,25,49,...
下面会说明,这个 block group 的个数受到限制。
可以试验证实:
# dumpe2fs /dev/sda1 (已省略大量信息)
Group 0: (Blocks 1-8192)
Group 1: (Blocks 8193-16384)
Group 2: (Blocks 16385-24576)
Group 3: (Blocks 24577-32768)
Group 4: (Blocks 32769-40960)
Group 5: (Blocks 40961-49152)
Group 6: (Blocks 49153-57344)
Group 7: (Blocks 57345-65536)
Group 8: (Blocks 65537-73728)
Group 9: (Blocks 73729-81920)
Group 10: (Blocks 81921-90112)
Group 11: (Blocks 90113-96357)
 
然后依次执行
# dd if=/dev/sda1 bs=1024 count=1 skip=1 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=8193 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=16385 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=24577 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=32769 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=40961 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=49153 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=57345 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=65537 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=73729 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=81921 | xxd | less
# dd if=/dev/sda1 bs=1024 count=1 skip=90113 | xxd | less
 
可以发现 Group 0, 1, 3, 5, 7, 9 数据全是一样的,
一个 super block 虽然存在于所有的 block group X 中,但是只有 block group 0 有效,
部分 super block 是备份,部分 super block 没有使用。
 
6.
计算 block group 的数量
我们发现,group 从 0 到 11,一共 12 块儿,那这个数值是怎么得来的?
 
我们应该用另外一种想法,去思考如果是你,你如何保存 group block 的块儿个数。
直接保存 数值12 吗?这样就缺乏灵活性了,因为 12 这个数值是由其他值得到的,
文件系统存储的就是元数据,而 12 不再是元数据了。
 
显然,用 使用的 block 数 / 每块儿占用的 block 数,就能得到一共有多少 group block 了。
2nd 数据域,总数是:s_blocks_count
9th  数据域,每个 group 所占 blocks:s_blocks_per_group
6th  数据域,第一个数据块儿:s_first_data_block
 
根据前面的16进制 的 super block 得到:(注意小端存放)
s_blocks_count = 6678 0100 = 16678H = 91768
s_blocks_per_group = 0020 0000 = 2000H = 8192
s_first_data_block = 0100 0000 = 1
 
所以得到 向下取整91768-1-1)/ 8192 是 11,因此必须要有 11 块儿,
至于剩下的 block 就再分给 一个 block group 了。所以 前 11块儿 block group 都有 s_blocks_per_group 个 block。
所以得到公示:
block group 的块儿数 = (s_blocks_count - s_first_data_block-1)/ s_blocks_per_group 取整+1
 
 
 
Part 2. Block Group Descriptor Table,n*32 B itself  / take up 1 block size
 
The block group descriptor table is an array of block group descriptor, used to define parameters of all
the block groups. It provides the location of the inode bitmap and inode table, block bitmap, number of
free blocks and inodes, and some other useful information.
The block group descriptor table is located on the first block following the superblock. This would be the
third block on a 1KiB block file system, or the second block for 2KiB and larger block file systems.
Shadow copies of the block group descriptor table are also stored with every copy of the superblock.

 

struct ext3_group_desc {  

__u32 bg_block_bitmap;/* block 指针指向 block bitmap */  

__u32 bg_inode_bitmap;/* block 指针指向 inode bitmap */  

__u32 bg_inode_table;/* block 指针指向 inodes table */  

__u16 bg_free_blocks_count;/* 空闲的 blocks 计数 */  

__u16 bg_free_inodes_count;/* 空闲的 inodes 计数 */  

/*10*/

__u16 bg_used_dirs_count;/* 目录计数 */  

__u16 bg_pad;/* 可以忽略 */  

__u32 bg_reserved[3];/* 可以忽略 */ 

};

0.
tabel 表的组成
0 号  block group desc,能找到 block group 0 的剩下的信息;
1 号  block group  desc,能找到 block group 1 的剩下的信息;
.......
11号  block group desc,能找到 block group 11 的剩下的信息,
把 block group 0-11 中的 Block gourp descriptor 合在一起就组成了 desc Table
 
故在 block group 0 的这个 table 中,汇聚了后面所有的 block group 1-X 的 Block gourp descriptor。
 
1.
拷贝备份
与 Super block 的备份原则一样,哪个 block group 中备份了 super block,那个 block group 也备份这个 table 表。
验证:
# dumpe2fs /dev/sda1 (已省略大量信息)
Group 0: (Blocks 1-8192)
Group 1: (Blocks 8193-16384)
Group 2: (Blocks 16385-24576)
Group 3: (Blocks 24577-32768)
Group 4: (Blocks 32769-40960)
Group 5: (Blocks 40961-49152)
Group 6: (Blocks 49153-57344)
Group 7: (Blocks 57345-65536)
Group 8: (Blocks 65537-73728)
Group 9: (Blocks 73729-81920)
Group 10: (Blocks 81921-90112)
Group 11: (Blocks 90113-96357)
 
# dd if=/dev/sda1 count=1 bs=1024 skip=2 | xxd | less
# dd if=/dev/sda1 count=1 bs=1024 skip=8194 | xxd | less
# dd if=/dev/sda1 count=1 bs=1024 skip=16386 | xxd | less
.......
即每个 skip 多加 1 即可,第一个 block 是 Super Block 用的,第二个就是 desc table
 
通过 Super block 和 Group des table 的备份原则,结合我的分区的情况,画图如下:

ext2 / ext3 结构分析 - lizeaaaa - Hello World
 
如此一来,即使 block group 0 的 Super Block 或 Group Desc Table 坏了,仍拥有备份。 
 
2.
block group 个数受限
整个 desc table 总大小不能超过 1个 block 的大小。
本例中 block 块儿大小为 1024B,每个 desc 是32B,所以最多能容纳 1024/32 = 32个
对于 4K 的 block,则最多容纳 1024B*4 / 32B = 128个
 
3.
内部的 3 个重要指针
我们拿出 block group 0 的 table:
 
# dd if=/dev/sda1 bs=1024 count=1 skip=2 | xxd | less
0000000: 0300 0000 0400 0000 0500 0000 0000 bd07  ................
0000010: 0200 0000 0000 0000 0000 0000 0000 0000  ................
0000020: 0320 0000 0420 0000 0520 0000 e919 d807  . ... ... ......
0000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000040: 0140 0000 0240 0000 0340 0000 491e c907  .@...@...@..I...
0000050: 0100 0000 0000 0000 0000 0000 0000 0000  ................
0000060: 0360 0000 0460 0000 0560 0000 011f d807  .`...`...`......
0000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000080: 0180 0000 0280 0000 0380 0000 031f d807  ................
0000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000a0: 03a0 0000 04a0 0000 05a0 0000 011f d807  ................
00000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000c0: 01c0 0000 02c0 0000 03c0 0000 031f d807  ................
00000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000e0: 03e0 0000 04e0 0000 05e0 0000 011f d807  ................
00000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000100: 0100 0100 0200 0100 0300 0100 031f d807  ................
0000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000120: 0320 0100 0420 0100 0520 0100 011f d807  . ... ... ......
0000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000140: 0140 0100 0240 0100 0340 0100 031f d807  .@...@...@......
0000150: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000160: 0160 0100 0260 0100 0360 0100 6817 d807  .`...`...`..h...
0000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s
00003f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
由于 2. 得知当 block size 为 1024B 时,最多包含 32 个 32B 的描述表,
现在只有 12 个 block group,所以只占用了 1024B 的 37.5% (11/32)
 
我们就拿出第一个进行分析:
0000000: 0300 0000 0400 0000 0500 0000 0000 bd07  ................
0000010: 0200 0000 0000 0000 0000 0000 0000 0000  ................
 
对用 结构体 ext3_group_desc 的成员,

__u32 bg_block_bitmap = 0300 0000 = 3 /* block 指针指向 block bitmap */  

__u32 bg_inode_bitmap = 0400 0000 = 4 /* block 指针指向 inode bitmap */  

__u32 bg_inode_table =0500 0000 = 5 /* block 指针指向 inodes table */  

__u16 bg_free_blocks_count = 0 /* 空闲的 blocks 计数 */  

__u16 bg_free_inodes_count = bd07 = 48391 /* 空闲的 inodes 计数 */  

__u16 bg_used_dirs_count = 2 /* 目录计数 */  

__u16 bg_pad; /* 可以忽略 */  

__u32 bg_reserved[3]; /* 可以忽略 */ 

 
内部的 3 个指针分别是前 3 个成员,他们存储的数值不是地址,而是 block 号
下面我们分别进入这些 block 并分析。
 
 
 
Part 3. Block Bitmap,take up 1 block size
The “Block Bitmap” is normally located at the first block, or second block if a superblock backup is
present, of the block group. Its official location can be determined by reading the “bg_block_bitmap” in
its associated group descriptor.
Each bit represent the current state of a block within that block group, where 1 means “used” and 0
“free/available”. The first block of this block group is represented by bit 0 of byte 0, the second by bit 1
of byte 0. The 8th block is represented by bit 7 (most significant bit) of byte 0 while the 9th block is
represented by bit 0 (least significant bit) of byte 1.
 
文档已经说得很清楚了,在某个的 block group 中:
1 表示 占用,0 表示 可用
第1个 block 用 byte 0 的 bit 0
第2个 block 用 byte 0 的 bit 1
第8个 block 用 byte 0 的 bit 7
第9个 block 用 byte 1 的 bit 0
 
这个区域就是一个 block 的 bit 大地图,用 0 1 来代表是否占用
有了它才能知道哪个 block 是可写数据的
 
0.
block 数量受限
显然,1Byte = 8bits,而 Block Bitmap 占用 1 个 block_size,所以整个 Block bitmap
能存放的 Block 开关状态位有 block_size * 8 个,
因此,
每个 block group 中,能分配的 block 的上限 = block_size * 8
每个 block group 中,最大空间 = block_size * 8 * block_size
 
1.
偷窥看看
从 Desc table 中看到,block group 0 的 Block bitmap 在 号码为 3 的 block,进去看看:
# dd if=/dev/sda1 count=1 bs=1024 skip=3 | xxd | less
0000000: ffff ffff ffff ffff ffff ffff ffff ffff  ................
all ffffs
00003f0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
意思是说,/dev/sda1 中的 block group 0 已经全部用完了(没有分配的 inode 就浪费了)
 
# dumpe2fs /dev/sda1
Group 0: (Blocks 1-8192)
  Primary superblock at 1, Group descriptors at 2-2
  Block bitmap at 3 (+2), Inode bitmap at 4 (+3)
  Inode table at 5-255 (+4)
  0 free blocks, 1981 free inodes, 2 directories
  Free blocks: 
  Free inodes: 28-2008
 
观察到,
Group 0: (Blocks 1-8192)     满足 1. 的计算方法,1K*8 = 8192
Free blocks:     已经没有了
Free inodes: 28-2008     倒还有一堆没用
 
 
 
Part 4. Inode Bitmap,take up 1 block size
 
The “Inode Bitmap” works in a similar way as the “Block Bitmap”, difference being in each bit
representing an inode in the “Inode Table” rather than a block.
There is one inode bitmap per group and its location may be determined by reading the
bg_inode_bitmap” in its associated group descriptor.
When the inode table is created, all the reserved inodes are marked as used. In revision 0 this is the first
11 inodes.
 
作用和 Block Bitmap 一样,都是一个超级大的 bit 地图,
只不过 inode bitmap 用 0 1 标记的是一个 inode,即下面的结构。
 
保留的 inode 会被标记成 1
因为并非所有的 block group 都会预设成占用全部的 inode 
 
0.
inode 数量受限
道理同 block bitmap
每个 block group 中,能分配的 inode 数量上限 = block_size * 8
 
限制 inode Table 的大小,下面可知,每个 inode 128 B,
最大 inode Table 支持 128B * block_size * 8
 
1.
偷窥看看
从 Desc table 中看到,block group 0 的 inode bitmap 在 号码为 4 的 block:
# dd if=/dev/sda1 count=1 bs=1024 skip=4 | xxd | less
0000000: ffff ff07 0000 0000 0000 0000 0000 0000  ................
all 0s
00000f0: 0000 0000 0000 0000 0000 00ff ffff ffff  ................
all ffffs
00003f0: ffff ffff ffff ffff ffff ffff ffff ffff  ................
 
# dumpe2fs /dev/sda1
Group 0: (Blocks 1-8192)
  Primary superblock at 1, Group descriptors at 2-2
  Block bitmap at 3 (+2), Inode bitmap at 4 (+3)
  Inode table at 5-255 (+4)
  0 free blocks, 1981 free inodes, 2 directories
  Free blocks: 
  Free inodes: 28-2008
 
既然没有 free block了,那就意味着这个 block group 就已经满了。
我们能看  Free inodes: 28-2008,
 
从上面的 16进制 代码中读出:
FFFF FF07 就是 
11111111 11111111 11111111 00000111
之所以这样,是因为这个和小端格式有关
 
这个 bitmap 保存的就是 inode,即下一部分数据。
 
 
 
Part 5. Inode Table,n*128 B / take up X block_size by s_inodes_per_group of Superblock 
 
The inode (index node) is a fundamental concept in the ext2 filesystem. Each object in the filesystem is
represented by an inode.
The inode table is used to keep track of every directory, regular file, symbolic link, or special file; their
location, size, type and access rights are all stored in inodes. There is no filename stored in the inode
itself, names are contained in directory files only.
There is one inode table per block group and it can be located by reading the bg_inode_table in its
associated group descriptor. There are s_inodes_per_group inodes per table.
Each inode contain the information about a single physical file on the system. A file can be a directory, a
socket, a buffer, character or block device, symbolic link or a regular file. So an inode can be seen as a
block of information related to an entity, describing its location on disk, its size and its owner.
 
inode 是最重要的一部分,包含了文件的权限,拥有者,时间信息,存储在哪些 block 上等信息
每个文件系统的对象,都被抽象成了 inode
 
在 inode 中,不包含文件名!名字仅在 目录 中包含!
 

struct ext3_inode {  

__u16 i_mode;/* File mode */  

__u16 i_uid;/* Low 16 bits of Owner Uid */  

__u32 i_size;/* 文件大小,单位是 byte */  

__u32 i_atime;/* Access time */  

__u32 i_ctime;/* Creation time */

/*10*/  

__u32 i_mtime;/* Modification time */  

__u32 i_dtime;/* Deletion Time */  

__u16 i_gid;/* Low 16 bits of Group Id */  

__u16 i_links_count;/* Links count */  

__u32 i_blocks;/* blocks 计数 */

/*20*/  

__u32 i_flags;/* File flags */ 

__u32 l_i_reserved1;/* 可以忽略 */

/*28*/  

__u32 i_block[EXT3_N_BLOCKS];/* 一组 block 指针 */

/*64*/  

__u32 i_generation;/* 可以忽略 */

__u32 i_file_acl;/* 可以忽略 */  

__u32 i_dir_acl;/* 可以忽略 */  

__u32 i_faddr;/* 可以忽略 */  

__u8 l_i_frag;/* 可以忽略 */  

__u8 l_i_fsize;/* 可以忽略 */  

__u16 i_pad1;/* 可以忽略 */

__u16 l_i_uid_high;/* 可以忽略 */  

__u16 l_i_gid_high;/* 可以忽略 */  

__u32 l_i_reserved2;/* 可以忽略 */ 

};

注意:inode 编号从 1 开始;block 编号从 0 开始
 
0.
inode 的数量
SuperBlock 中有一个 s_inodes_per_group 指定了 每个 Block group 中含有多少 inode
不过这个数量不会超过 8 * block_size
如我的这个 /dev/sda1 中 block_size 为 1K,所以不会超过 8192 个,这个由 bitmap 限制
 
1.
详细分析 inode
我打开连续的 2 个 inode table 看,边看边分析。
由于每个 inode 是128B,所以下面用 颜色 进行区分,下划线是 i_block 区域
 
# dd if=/dev/sda1 bs=1024 count=2 skip=5 | xxd | less
inode table of inode 1
0000000: 0000 0000 0000 0000 f92a 254f f92a 254f  .........*%O.*%O
0000010: f92a 254f 0000 0000 0000 0000 0000 0000  .*%O............
0000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
我们发现,inode 1 很奇怪,block 区域没有存储任何指向 block 号的指针
 
原因在于 Super Block 中有一个字段:s_rev_level
ext2 / ext3 结构分析 - lizeaaaa - Hello World 
 
当这个字段取值为 0,则规定 inode table 中的前 11 个保留;
如果这个字段取值 1,则规定 inode table 需要查看 Superblock 中的 s_first_ino 字段
 
SuperBlock 中对的 s_first_ino 解释:
32bit value used as index to the first inode useable for standard files. In revision 0, the first non-reserved
inode is fixed to 11 (EXT2_GOOD_OLD_FIRST_INO). In revision 1 and later this value may be set to any
value.
 
通过查看 s_first_ino,发现值为 b,即 11
就是说前 10 个全是保留给系统使用的。
 
那么前 10 个都做了些什么呢?文档中说:
ext2 / ext3 结构分析 - lizeaaaa - Hello World
 
inode 2,后面解释根目录;因此所有的分区,跟目录都是 inode 2
inode 5,是 boot loader inode
 
 
inode 2
0000080: ed41 0000 0004 0000 b1ae 354f 65b5 344f  .A........5Oe.4O
0000090: 65b5 344f 0000 0000 0000 0400 0200 0000  e.4O............
00000a0: 0000 0000 0000 0000 0001 0000 0000 0000  ................
00000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
通过上面的分析,这里就是 分区根目录
它使用了 100H block 来存储数据
 
0000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s
0000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000180: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s
00001f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000200: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s
0000270: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000280: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s
00002f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000300: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s
0000370: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
inode 8
0000380: 8081 0000 0000 4000 0000 0000 f92a 254f  ......@......*%O
0000390: f92a 254f 0000 0000 0000 0100 2420 0000  .*%O........$ ..
00003a0: 0000 0000 0000 0000 0d01 0000 0e01 0000  ................
00003b0: 0f01 0000 1001 0000 1101 0000 1201 0000  ................
00003c0: 1301 0000 1401 0000 1501 0000 1601 0000  ................
00003d0: 1701 0000 1801 0000 1901 0000 1a02 0000  ................
00003e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00003f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
这里是 ext3 的日志(journal)区,可以使用 dumpe2fs /dev/sda1 :
dumpe2fs /dev/sda1 
......
Journal UUID:             <none>
Journal inode:            8
Journal device:           0x0000
.......
看到他用了 12 个直接block 指向,1 个 1级block,1 个 2级block指向,3级block指向 暂时没使用。
关于 “n级block指向” 的翻译方法很多,这只是我的翻译,保持你自己的翻译即可。下面是其结构图:
 
ext2 / ext3 结构分析 - lizeaaaa - Hello World
 
 
0000400: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s
0000470: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000480: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s
00004f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
inode 11
0000500: c041 0000 0030 0000 4caa 354f f92a 254f  .A...0..L.5O.*%O
0000510: f92a 254f 0000 0000 0000 0200 1800 0000  .*%O............
0000520: 0000 0000 0000 0000 0101 0000 0201 0000  ................
0000530: 0301 0000 0401 0000 0501 0000 0601 0000  ................
0000540: 0701 0000 0801 0000 0901 0000 0a01 0000  ................
0000550: 0b01 0000 0c01 0000 0000 0000 0000 0000  ................
0000560: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000570: 0000 0000 0000 0000 0000 0000 0000 0000  ................
通过上面的分析,我们得知 inode 11 才是 留给用户使用的
我们看看第一个的具体含义
c041,即 41c0,权限,表如下:
 
ext2 / ext3 结构分析 - lizeaaaa - Hello World
 
41c0,目录,user read,c=8+4,即 user wirte + user execute。即 drwx------
有兴趣的话,尝试翻译剩下的 inode 的其他字段
 
inode 12
0000580: a481 0000 445a 0000 29be 5a3e 1e2b 254f  ....DZ..).Z>.+%O
0000590: 29be 5a3e 0000 0000 0000 0100 3000 0000  ).Z>........0...
00005a0: 0000 0000 0000 0000 1f11 0000 2011 0000  ............ ...
00005b0: 2111 0000 2211 0000 2311 0000 2411 0000  !..."...#...$...
00005c0: 2511 0000 2611 0000 2711 0000 2811 0000  %...&...'...(...
00005d0: 2911 0000 2a11 0000 2b11 0000 0000 0000  )...*...+.......
00005e0: 0000 0000 9d04 7138 0000 0000 0000 0000  ......q8........
00005f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
inode 13
0000600: a481 0000 2253 0000 29be 5a3e 1e2b 254f  ...."S..).Z>.+%O
0000610: 29be 5a3e 0000 0000 0000 0100 2c00 0000  ).Z>........,...
0000620: 0000 0000 0000 0000 3711 0000 3811 0000  ........7...8...
0000630: 3911 0000 3a11 0000 3b11 0000 3c11 0000  9...:...;...<...
0000640: 3d11 0000 3e11 0000 3f11 0000 4011 0000  =...>...?...@...
0000650: 4111 0000 4211 0000 4311 0000 0000 0000  A...B...C.......
0000660: 0000 0000 9e04 7138 0000 0000 0000 0000  ......q8........
0000670: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
inode 14
0000680: a481 0000 c016 0000 081b 323e 4a2b 254f  ..........2>J+%O
0000690: 081b 323e 0000 0000 0000 0100 0c00 0000  ..2>............
00006a0: 0000 0000 0000 0000 4d11 0000 4e11 0000  ........M...N...
00006b0: 4f11 0000 5011 0000 5111 0000 5211 0000  O...P...Q...R...
00006c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00006d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00006e0: 0000 0000 9f04 7138 0000 0000 0000 0000  ......q8........
00006f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
inode 15
0000700: a481 0000 6402 0000 081b 323e 4a2b 254f  ....d.....2>J+%O
0000710: 081b 323e 0000 0000 0000 0100 0200 0000  ..2>............
0000720: 0000 0000 0000 0000 5311 0000 0000 0000  ........S.......
0000730: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000740: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000750: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000760: 0000 0000 a004 7138 0000 0000 0000 0000  ......q8........
0000770: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
inode 16
0000780: a481 0000 8002 0000 081b 323e 4a2b 254f  ..........2>J+%O
0000790: 081b 323e 0000 0000 0000 0100 0200 0000  ..2>............
00007a0: 0000 0000 0000 0000 5411 0000 0000 0000  ........T.......
00007b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00007c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00007d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00007e0: 0000 0000 a104 7138 0000 0000 0000 0000  ......q8........
00007f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
因为我们用 dd 读取了 conut=2,即读取了 2048 B,
而每个 inode 为 128 B,所以 2048/128 = 16 个 inode
即对于 1K 的 block_size,只能存放 8 个 inode
 
 
2.
关于 inode 的计算
因为 inode 是分区内唯一的,是顺序的,而又能在 Super bolck 中知道 每个 block group 中的 inode 数,故:
block group           = (inode - 1) / s_inodes_per_group
local inode index = (inode - 1) % s_inodes_per_group
用这 2 和公式就能找到硬盘中任意的文件的 Inode 信息了
 
ext2 / ext3 结构分析 - lizeaaaa - Hello World 
 
 
 
Part 5. Block Data
这部分就全是是 block 了,专门存放数据用的,
关于这里面的格式,下面讲。
 
 
 
Other 1. Directory Structure
下面我们来看看 inode 2 中所指向的 block 中的信息:
 
inode 2
0000080: ed41 0000 0004 0000 b1ae 354f 65b5 344f  .A........5Oe.4O
0000090: 65b5 344f 0000 0000 0000 0400 0200 0000  e.4O............
00000a0: 0000 0000 0000 0000 0001 0000 0000 0000  ................
00000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
# dd if=/dev/sda1 count=1 bs=1024 skip=256 | xxd | less
(这个 100H 是 data block 所在的第一个block
0000000: 0200 0000 0c00 0102 2e00 0000 0200 0000  ................
0000010: 0c00 0202 2e2e 0000 0b00 0000 1400 0a02  ................
0000020: 6c6f 7374 2b66 6f75 6e64 0000 b10f 0000  lost+found......
0000030: 0c00 0402 6772 7562 0d00 0000 1800 0a01  ....grub........
0000040: 6d65 7373 6167 652e 6a61 3235 3262 3031  message.ja252b01
0000050: 0c00 0000 1000 0701 6d65 7373 6167 6500  ........message.
0000060: 1200 0000 1800 0f01 636f 6e66 6967 2d32  ........config-2
0000070: 2e34 2e32 302d 3831 0e00 0000 1000 0601  .4.20-81........
0000080: 626f 6f74 2e62 0000 0f00 0000 1000 0701  boot.b..........
0000090: 6368 6169 6e2e 6200 1000 0000 1000 0701  chain.b.........
00000a0: 6f73 325f 642e 6200 1300 0000 2400 1401  os2_d.b.....$...
00000b0: 6d6f 6475 6c65 2d69 6e66 6f2d 322e 342e  module-info-2.4.
00000c0: 3230 2d38 3262 3031 3262 3031 1100 0000  20-82b012b01....
00000d0: 1c00 1301 5379 7374 656d 2e6d 6170 2d32  ....System.map-2
00000e0: 2e34 2e32 302d 3800 1600 0000 1000 0707  .4.20-8.........
00000f0: 766d 6c69 6e75 7a2d 1700 0000 1400 0a07  vmlinuz-........
0000100: 5379 7374 656d 2e6d 6170 6230 1400 0000  System.mapb0....
0000110: 1800 1001 766d 6c69 6e75 782d 322e 342e  ....vmlinux-2.4.
0000120: 3230 2d38 1500 0000 1800 1001 766d 6c69  20-8........vmli
0000130: 6e75 7a2d 322e 342e 3230 2d38 1800 0000  nuz-2.4.20-8....
0000140: 1400 0b07 6d6f 6475 6c65 2d69 6e66 6f00  ....module-info.
0000150: 1900 0000 1000 0801 6b65 726e 656c 2e68  ........kernel.h
0000160: 1a00 0000 1c00 1301 696e 6974 7264 2d32  ........initrd-2
0000170: 2e34 2e32 302d 382e 696d 6700 1b00 0000  .4.20-8.img.....
0000180: 8402 0101 3100 0000 0000 0000 0000 0000  ....1...........
all 0s
00003f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
右边的是 ascii 的翻译,总算看到了点文字啊。
 
# df
文件系统               1K-块        已用     可用 已用% 挂载点
/dev/sda2             13322104   1860548  10784820  15% /
/dev/sda1                93309      9427     79065  11% /boot
none                    127632         0    127632   0% /dev/shm
 
可以看到那些 acsii 刚好就是 /boot 下的文件,而 /boot 就是 /dev/sda1 的根目录
 
对于一个目录,在 data block 中存放的格式为:
 
 

 

struct ext3_dir_entry_2 {  
__u32 inode;/* Inode 号数 */  
__u16 rec_len;/* Directory entry length */  
__u8  name_len;/* Name length */  
__u8  file_type;  
char  name[EXT3_NAME_LEN];/* File name */ 
};
这是一个变长的格式,长度由 rec_len 指定。
 
0.
我们来分析这些的 16进制数:
0000000: 0200 0000 0c00 0102 2e00 0000  0200 0000  ................
0000 0002  表示这个文件所连接到的 inode 号码,这个号码就是2,就是 /boot 的 inode
000c  表示这个结构体的长度,12。这个数值是 4字节 的倍数,注意:如果有一个 ext3_dir_entry_2 存不下了,它不会被截断,而是往下一个 block 存
01  表示表示 name 有多少字节。
02  表示是一个目录。文件类型看下表:
 
ext2 / ext3 结构分析 - lizeaaaa - Hello World
 
2e00 0000 就是文件名了,字符串是不按照小端格式翻过来的,也就是 . \0 \0 \0,后面的 \0 是填充到 4B 倍数。
 
1.
查看内容
0000000: 0200 0000 0c00 0102 2e00 0000 0200 0000  ................
0000010: 0c00 0202 2e2e 0000 0b00 0000 1400 0a02  ................
0000 0002  inode 号码
00c0  12B
02  name is 2B
02  是个目录
2e2e 0000  目录名 ..
可以看出,根目录下的 2 个目录 . 和 .. 都指向相同的 inode 2
 
 
                                               0b00 0000 1400 0a02  ................
0000020: 6c6f 7374 2b66 6f75 6e64 0000 b10f 0000  lost+found......
0000030: 0c00 0402 6772 7562 0d00 0000 1800 0a01  ....grub........
0000040: 6d65 7373 6167 652e 6a61 3235 3262 3031  message.ja252b01
0000050: 0c00 0000 1000 0701 6d65 7373 6167 6500  ........message.
0000060: 1200 0000 1800 0f01 636f 6e66 6967 2d32  ........config-2
0000070: 2e34 2e32 302d 3831 0e00 0000 1000 0601  .4.20-81........
0000080: 626f 6f74 2e62 0000 0f00 0000 1000 0701  boot.b..........
0000090: 6368 6169 6e2e 6200 1000 0000 1000 0701  chain.b.........
00000a0: 6f73 325f 642e 6200 1300 0000 2400 1401  os2_d.b.....$...
00000b0: 6d6f 6475 6c65 2d69 6e66 6f2d 322e 342e  module-info-2.4.
00000c0: 3230 2d38 3262 3031 3262 3031 1100 0000  20-82b012b01....
00000d0: 1c00 1301 5379 7374 656d 2e6d 6170 2d32  ....System.map-2
00000e0: 2e34 2e32 302d 3800 1600 0000 1000 0707  .4.20-8.........
00000f0: 766d 6c69 6e75 7a2d 1700 0000 1400 0a07  vmlinuz-........
0000100: 5379 7374 656d 2e6d 6170 6230 1400 0000  System.mapb0....
0000110: 1800 1001 766d 6c69 6e75 782d 322e 342e  ....vmlinux-2.4.
0000120: 3230 2d38 1500 0000 1800 1001 766d 6c69  20-8........vmli
0000130: 6e75 7a2d 322e 342e 3230 2d38 1800 0000  nuz-2.4.20-8....
0000140: 1400 0b07 6d6f 6475 6c65 2d69 6e66 6f00  ....module-info.
0000150: 1900 0000 1000 08 01 6b65 726e 656c 2e68  ........kernel.h
.....
 
2.
再举一例
下面我们分析一个 一般的文件 而不是文件夹:
0000150: 1900 0000 1000 08 01 6b65 726e 656c 2e68  ........kernel.h
1900 0000,19H,25,inode 25
1000,10H,16,结构体 16 字节
08,name 8 B
01,Regular File,一般文件
6b65 726e 656c 2e68,kernel.h
 
3.
进入一个 inode 中
我们已经看到上面的 inode 25,所以只要进入 inode 25,查看其 block 号码,就能找到文件了
我们要进入 inode 25,就要算出来 inode 25 在那个 block 中:
/dev/sda1 按照顺序排:
1024 B ,什么也不做,为了可移植
1 个 Super block,占用 1 block_size
1 个 Desc table,占用 1 block_size
1 个 Block bitmap,占用 1 block_size
1 个 Inode birmap,占用 1 block_size
接下来就是 n 个 block_size 的 Inode table,一个 block 中能能存放 numOfIno = block_size / 128B 个 inode
inode 25 所在 block 号码 = 1024B / 1024B + 4 + 25/8 + 1 = 9
在 该block 中的 offset:128B * (1-1) = 0 B
 
我们 dd 一下
# dd if=/dev/sda1 count=1 bs=1024 skip=8 | xxd | less
# #因为 inode 25 在 block 9,所以跳过 个 1024字节
0000000: a481 0000 d901 0000 3ed1 354f 5a2b 254f  ........>.5OZ+%O
0000010: 5a2b 254f 0000 0000 0000 0100 0200 0000  Z+%O............
0000020: 0000 0000 0000 0000 1c25 0000 0000 0000  .........%......
0000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000060: 0000 0000 aa04 7138 0000 0000 0000 0000  ......q8........
0000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
0000080: a481 0000 d6e0 0300 5c2b 254f 5c2b 254f  ........\+%O\+%O
.......
00003f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
 
我们只关心 offset = 0 处,然后往下数 128 字节,用红色标记,
看到 inode 25 只使用了 251c 这 1 个 1级block指向。
 
我们打开,inode 25:
# dd if=/dev/sda1 count=1 bs=1024 skip=9500 | xxd | less
 
0000000: 2f2a 2054 6869 7320 6669 6c65 2069 7320  /* This file is 
0000010: 6175 746f 6d61 7469 6361 6c6c 7920 6765  automatically ge
0000020: 6e65 7261 7465 6420 6174 2062 6f6f 7420  nerated at boot 
.......
00001c0: 524e 454c 5f55 5020 310a 2365 6e64 6966  RNEL_UP 1.#endif
00001d0: 0a0a 2365 6e64 6966 0a00 0000 0000 0000  ..#endif........
00001e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
all 0s
00003f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
全部都是 C 语言,因为那是一个 kernel.h 文件嘛!
 
4.
我们看到,inode 25 所指向的 block 9500 没有用完,里面还有很多 0 的空间
这就是 内部碎片,是所有文件系统都无法避免的。
 
 
5.
一个总结
这说明了,对于一个 文件inode,其所指向的 block 中直接存放数据
而对于一个 目录inode,其所指向的 block 实际上存放的一个 ext3_dir_entry_2 的结构体数组,这个数组中存放了文件名了 其指向的 Inode。
再次说明,文件名是包含在 ext3_dir_entry_2 结构体中的。 


至此,我们从从文件系统一直追更溯源到一个文件。
 
显然,文章里面罗列的都是 数据结构,算法能把这个结构体真正地“活”起来,
虽然明白了 ext2/ext3 的基本结构,但是组织这些数据结构的算法的学习,仍然是任重道远啊。