ext2 / ext3 结构分析


原文:ext2 / ext3 结构分析(第 1 部分)
http://bbs.chinaunix.net/thread-3669811-1-1.html



《ext2 / ext3 结构分析》


字数2-20000...分开发吧。。
---------------------------------------------------------------------------------------------------------------------------------------
实验机器:虚拟机 + red hat 9

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

ext2/ext3 结构图:
123.PNG
2012-02-11 20:10 上传
下载附件 (17.42 KB)



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-3276
Group 4: (Blocks 32769-40960)
Group 5: (Blocks 40961-49152)
Group 6: (Blocks 49153-57344)
Group 7: (Blocks 57345-65536)
Group 8: (Blocks 65537-7372
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-3276
Group 4: (Blocks 32769-40960)
Group 5: (Blocks 40961-49152)
Group 6: (Blocks 49153-57344)
Group 7: (Blocks 57345-65536)
Group 8: (Blocks 65537-7372
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 的备份原则,结合我的分区的情况,画图如下:
777.PNG
2012-02-11 20:11 上传
下载附件 (37.61 KB)


如此一来,即使 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,即下一部分数据。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值