深入理解ext4(一)----extent区段

来源:互联网 发布:淘宝互刷是怎么刷的 编辑:程序博客网 时间:2024/04/29 18:41

 文件系统是操作系统的一个重要组成部分,也有着举足轻重的地位。本系列文章主要讲述了linux ext4文件系统的一些实现原理。笔者参考了2.6.32.60的内核源代码。在写这篇的文章时,最新的内核已经去到了3.7.9。

ext4是替代ext2/3Linux文件系统。从2.6.28版本开始,被正式认定进入稳定(stable)。本文主要介绍ext4文件系统在硬盘层面上的存储结构及原理。

 

读者对象:对Linux有一定基础,希望了解ext4底层原理,和它与ext2/3系统区别。

 

关键词汇

先回顾几个基本的概念,如果不是特别清楚下面几个概念的话,可以去google一下。

inode:索引节点

superblock:超级块

block:文件系统块

block group:文件系统块组

disk block:磁盘块(512字节)

block device:块设备

VFS:虚拟文件系统

EXT4存储结构

假如把整个超级块比如一本书,那么文件系统的工作就是把要记录的内容,按页码,行段记录在这本书里。这当然也包括书的目录了。我们使用dumpe2fs工具输出:

 

Reserved GDT blocks:      609

Blocks per group:         32768

Fragments per group:      32768

Inodes per group:         8128

Inode blocks per group:   508

Flex block group size:    16

Filesystem created:       Mon May 14 13:30:51 2012

Last mount time:          Sun Jan  6 18:51:16 2013

Last write time:          Sun Jan  6 18:51:16 2013

Mount count:              282

Maximum mount count:      -1

Last checked:             Mon May 14 13:30:51 2012

Check interval:           0 (<none>)

Lifetime writes:          20 GB

Reserved blocks uid:      0 (user root)

Reserved blocks gid:      0 (group root)

First inode:              11

Inode size:               256

Required extra isize:     28

Desired extra isize:      28

Journal inode:            8

 

首先,映入眼帘的是该超级块的相关重要参数,比如inode大小,块组含块数,块组inode数目等等。这些数据是存在ext4_super_blockext4_sb_info这两个结构体中,定义在ext4.h头文件里,它们不是本篇重要讨论的内容。我们只需要知道它们是存放一些超级块信息的结构体即可。

接下来,可以看到ext4硬盘上的存储结构:

Group 0: (Blocks 0-32767) [ITABLE_ZEROED]

  Checksum 0x7cf3, unused inodes 0

  Primary superblock at 0, Group descriptors at 1-2

  Reserved GDT blocks at 3-611

  Block bitmap at 612 (+612), Inode bitmap at 628 (+628)

  Inode table at 644-1151 (+644)

  2720 free blocks, 0 free inodes, 1383 directories

  Free blocks: 8888-8959, 9068, 9071-9135, 9144-9175, 9200-9207, 9213-9214, 9279, 9700-10120, 11823-11964, 12213-12870, 12879-13043, 13139-13254, 18432-19021, 22748-22975, 32549-32767

  Free inodes:

 

这是块组0的情况,它表明块组0由块号为0-3276732768个块组成,超级块基本信息存在块0,块组描述符在块1-2,预留的块组描述符表在块3-611,块位图在块612中,inode位图在块628中,Inode表在块644-1151中,空闲的块有很多,空闲的inode没了。

接下来,我们将重点分析这句废话中每个词的含义

        超级块基本信息:

                  我们在前面已经讲过了。顾名思义,不多解释。

         块组描述符

                  在内核中就是结构体ext4_group_desc,它包括的内容为:块位图块号,inode位图块号,inode表块号,空闲块计数,自由块计数等等。

         预留的块组描述符表

                  为以后要使用所留下来的空间。

         块位图

                  这个就是一个块使用情况记录表。记录哪些块使用,哪些块未使用。它的原理就是对整个块组中0-32767这总共32768个块中作一个映射。根据一个bytes8个位00000000,一个块有4096bytes也就是有4096*8=32768个位,这32768个位刚好对应了块组中32768的块。如果第N个块被使用了则标记第N位为1,否则为0

         inode位图

                  和上面的块位图一样,这个是inode的使用情况记录表。由超级块基本信息可以看到每个块组有8128inode,这里对inode的映射原理和块位图也是一样,只不过         没有用满一个块。

         inode

                  inode表就是具体存放inode信息的地方。在ext4中,inode的大小为256字节(ext2/3中仅有它的一半,128字节),一个块可以存放16inode,由于一个块组有8128          inode,总共需要8128/16=508个块存放inode表。这个值可以在超级块基本信息中的Inode blocks per group中看到。

讲完了这些词的含义,我们对group 0有了初步的了解。那么group 1呢?

Group 1: (Blocks 32768-65535) [ITABLE_ZEROED]

  Checksum 0xbb99, unused inodes 0

  Backup superblock at 32768, Group descriptors at 32769-32770

  Reserved GDT blocks at 32771-33379

  Block bitmap at 613 (+4294935141), Inode bitmap at 629 (+4294935157)

  Inode table at 1152-1659 (+4294935680)

  598 free blocks, 0 free inodes, 648 directories

  Free blocks: 33424-33439, 33442-33443, 33564-33627, 33644-33647, 33652-33663, 33725-33871, 33878-33931, 33934-33973, 33976-33983, 34046-34047, 34176-34303, 36008, 36015, 36019, 36412, 40299-40415

  Free inodes:

 

我们看到group 1 中,primary superblock 变为了backup superblock,由于超级块基本信息对于文件系统至关重要,为了系统的健壮性,ext文件系统在每个块组中都进行了备份。ext4考虑到在每个块组中都备份有点多余,尤其是组描述符表所以就仅在块号以357为幂的块组上进行备份。

用个表格表示超级块中块组的结构:

ext4 超级块

块组描述符

Reserved GDT Blocks

数据块位图

Inode位图

inode 表

数据块

1 block

若干blocks

若干 blocks

1 block

1 block

若干

好多好多块

                                                                                                                                                    

inode

Purpose

0

Doesn't exist; there is no inode 0.

1

List of defective blocks.

2

Root directory.

3

ACL index.

4

ACL data.

5

Boot loader.

6

Undelete directory.

7

Reserved group descriptors inode.

8

Journal inode.

11

First non-reserved inode. Usually this is the lost+found directory.

 

块寻址

ext4的块寻址已经改为48位。这种设计改动是为了支持更大的文件系统大小。EXT4使用了区段(extents)这个概念,取代了过去早期UNIX文件系统中(ext2/3)中低效的非直接块映射机制。区段和NTFS上的cluster有点类似,它们都是选定了一个特定的块地址并把数个块组合一个区间。一个文件如果是碎片化的,那么就意味它着拥有多个区段, ext4会尽它自己的努力保持文件连续。

 

这种新的块寻址策略导致了先前工具的大部分问题。举个列子:

 

[root@localhost Desktop]# stat math.c

  File: `math.c'

  Size: 1477               Blocks: 8          IO Block: 4096   regular file

Device: fd00h/64768d     Inode:420402      Links: 1

Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)

Access: 2013-01-05 15:07:11.815541582 +0800

Modify: 2012-08-20 13:40:02.496797954 +0800

Change: 2012-12-30 11:28:54.751357610 +0800

 

 


由上面得到文件math.cInode号为420402

[root@localhost Desktop]# istat /dev/mapper/VolGroup-lv_root 420402

inode: 420402

Allocated

Group: 51

Generation Id: 1062005310

uid / gid: 0 / 0

mode: rrw-r--r--

Flags:

size: 1477

num of links: 1

 

Inode Times:

Accessed:         2013-01-05 15:07:11 (CST)

File Modified:   2012-08-20 13:40:02 (CST)

Inode Modified:        2012-12-30 11:28:54 (CST)

 

Direct Blocks:

127754

 

 


由上面的命令结果可以看到,Inode位于节点上块组51上,留意上面命令最下面有Direct Blocks这一行,这一行写着127754。在ext4的文件系统中,由于direct block映射的块寻址机制被取代,而采取的是extent区段树的块寻址。这个地方的值基本上是无效的。127754这个值十六进制表示为0x1f30a,我们在稍后的讨论这个值的来源。

我们知道了math.c这个文件的inode号码为420402,那么怎样知道它数据块是拿一个呢?

由前面的内容我们知道,每个块可以存16inode,那么420402则在第420402/16=26275.125个块中,也就是位于第26275个块的第二个位置。每个块组有508inode块,那么26725/508=51.72可以得知,位于块组51号之中,这个值可以在我们之前istat中可以验证。

那么具体是51块组中的哪个块呢?我们先确定这个inode块是在块组中的第几个块。因为每个块组有508inode块,51块组前面共有51*508=25908个块。第26275inode块在51块组中排在26275-25908=367的位置。查看51块组的描述:

 

Group 51: (Blocks 1671168-1703935) [ITABLE_ZEROED]

  Checksum 0x5ffd, unused inodes 0

  Block bitmap at 1572867 (+4294868995), Inode bitmap at 1572883 (+4294869011)

  Inode table at 1574420-1574927 (+4294870548)

  34 free blocks, 1 free inodes, 541 directories

  Free blocks: 1672899, 1673339, 1673344, 1674035, 1674054, 1674062, 1674077, 16

74334, 1674353-1674354, 1674423, 1675259, 1675754-1675755, 1675763, 1675860-1675

861, 1675867, 1675979, 1676183, 1676287, 1676367, 1676507, 1676526-1676527, 1676

567, 1676711, 1676743, 1676924-1676925, 1676934, 1691649, 1691658, 1691707

  Free inodes: 422608

 

看到inode的起点位于1574420,由此,我们想要找的inode信息的块就存在于1574787inode块中的第二个。

可以使用blkcat查看1574787的内容,我们用vi切换到16进制模式打开如下:

 

0000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................

0000080: 1c00 0000 4430 c1a7 c8f1 733d fc8a 6cb1  ....D0....s=..l.

0000090: 58a8 b04f 04a0 6538 0000 0000 0000 02ea  X..O..e8........

00000a0: 0706 3c00 0000 0000 2200 0000 0000 0000  ..<.....".......

00000b0: 7365 6c69 6e75 7800 0000 0000 0000 0000  selinux.........

00000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................

00000d0: 0000 0000 0000 0000 0000 0000 7379 7374  ............syst

00000e0: 656d 5f75 3a6f 626a 6563 745f 723a 6164  em_u:object_r:ad

00000f0: 6d69 6e5f 686f 6d65 5f74 3a73 3000 0000  min_home_t:s0...

0000100: a481 0000 c505 0000 1fd1 e750 f6b4 df50  ...........P...P

0000110: b2cd 3150 0000 0000 0000 0100 0800 0000  ..1P............

0000120: 0000 0800 0100 0000 0af3 0100 0400 0000  ................

0000130: 0000 0000 0000 0000 0100 0000 d64a 1c00  .............J..

0000140: 0000 0000 0000 0000 0000 0000 0000 0000  ................

 

 我们知道inodesize大小为256字节,那么第二个inode的起始位置也就是256=0x100处。

这个时候,我们看下inode的数据结构:

 

位置

名称

描述

0x0

__le16

i_mode

文件模式

0x2

__le16

i_uid

所有者UID.

0x4

__le32

i_size_lo

文件大小.

0x8

__le32

i_atime

读取时间.

0xC

__le32

i_ctime

Inode修改时间

0x10

__le32

i_mtime

文件修改时间.

0x14

__le32

i_dtime

删除时间

0x18

__le16

i_gid

GID.

0x1A

__le16

i_links_count

硬链接计数.

0x1C

__le32

i_blocks_lo

块计数(512字节)

0x20

__le32

i_flags

文件标识(ext4使用extent需要标记0x80000)

...

0x28

__le32

i_block[EXT4_N_BLOCKS=15]

块映射(ext2/3)或区段树(ext4)

...

 

我们按照表中的结构,对照上面的块码:

 

偏移

大小

名称

描述

0x0

0x81a4

i_mode

文件模式

0x2

0x0000

i_uid

所有者UID.

0x4

0x0000 05c5

i_size_lo

文件大小.

0x8

0x50e7 d11f

i_atime

读取时间.

0xC

0x50df b4f6

i_ctime

Inode修改时间

0x10

0x5031 b2cd

i_mtime

文件修改时间.

0x14

0x0000 0000

i_dtime

删除时间

0x18

0x0000

i_gid

GID.

0x1A

0x0001

i_links_count

硬链接计数.

0x1C

0x0000 0008

i_blocks_lo

块计数(512字节)

0x20

0x0080 0000

i_flags

文件标识(ext4使用extent需要标记0x80000)

...

0x28

 ...

i_block[EXT4_N_BLOCKS=15]

块映射(ext2/3)或区段树(ext4)

...

 

细心的同学会发现大小的顺序是倒过来的,这是因为__lexx类型,lelittle endian小端开始的缩写,意思就是从小到大的顺序。我们看到文件的大小为0x5c5=1477,这说明我们找的正是math.cinode

 

因为ext4 使用区段去代替了块映射去查找文件的内容。从40-9960个字节过去是用作块映射的,如今用作存储extent信息。extent结构体有12字节的大小,反应快的同学马上会说,那么一个inode可以存放最多5extent。然而这是不对的,因为前12个字节(40-51)被段头(extent header)所占据,所以,一个inode中的区段数最多只能是4

 

现在,我们重点开始讲区段树(extent tree)

ext4中,区段树取代了旧式的逻辑块映射。这是因为在老的模式中,分配连续的1000个块需要映射这1000个块的地址。但使用了区段,只需要映射一个区段并把区段的长度标记为1000ee_len=1000)。如果起用了flex_bg的功能,一个区段可以分配一个很大的文件,这降低了元数据的大小,也提高了硬盘的效率。inode必须使用区段标记0x80000开启区段的功能。

区段的结构是树形的。每个树节点的起始为:struct ext4_extent_header(这是一个结构体,我们接下来会给大家描述它的内容)。如果一个节点是树的内部节点(即eh.eh_depth>0),那么eh.eh_entries的指针将指向struct ext4_extent_idx;每个这些索引都指向一个块,块中包含更多的区段树中的节点。如果节点是树的叶子节点(eh.eh_depth=0),那么eh.eh_entries的指针将指向struct ext4_extent;这些结构体中指向文件的数据块。区段树的根节点存在inode.i_block,也就是我们在前面讨论的从40-99的那60个字节里。

 

说了这么多,我们还是赶紧看看extent的结构吧;

 

首先出场的是段头(extent header)

 

偏移

大小

名称

描述

0x0

__le16

eh_magic

幻数magic number, 0xF30A.

0x2

__le16

eh_entries

区段数.

0x4

__le16

eh_max

最大的区段数.

0x6

__le16

eh_depth

段节点在段树中的深度。0则表示为叶子节点,指向数据块;否则指向其它段节点。

0x8

__le32

eh_generation

暂不讨论

 

同样的,对照我们的实际数据看看

 

偏移

大小

名称

描述

0x0

0xf30a

eh_magic

幻数magic number, 0xF30A.

0x2

0x0001

eh_entries

区段数.

0x4

0x0004

eh_max

最大的区段数.

0x6

0x0000

eh_depth

段节点在段树中的深度。0则表示为叶子节点,指向数据块;否则指向其它段节点。

0x8

0x0000 0000

eh_generation

暂不讨论

 

 

接下来我们先看struct ext4_extent_idx,这个结构在前面我们有提到过,用于extent树的内部节点。

 

偏移

大小

名称

描述

0x0

__le32

ei_block

逻辑块号.

0x4

__le32

ei_leaf_lo

区段树中下一层的区段节点块地址(低32位),可以指向叶子节点或者内部节点。

0x8

__le16

ei_leaf_hi

上一栏的高16位地址

0xA

__u16

ei_unused

未使用

 

我们接着看struct ext4_extent,叶子节点的结构体

 

偏移

大小

名称

描述

0x0

__le32

ee_block

此区段的第一个块号,起始块号

0x4

__le16

ee_len

区段内包含的块数.

0x6

__le16

ee_start_hi

此区段所指向的块号(高16位)

0x8

__le32

ee_start_lo

此区段所指向的块号(低32位)

 

对照我们的实际数据看看

 

偏移

大小

名称

描述

0x0

0x0000 0000

ee_block

此区段的第一个块号,起始块号

0x4

0x0001

ee_len

区段内包含的块数.

0x6

0x0000

ee_start_hi

此区段所指向的块号(高16位)

0x8

0x001c 4ad6

ee_start_lo

此区段所指向的块号(低32位)

 

由上表可以看到,因为我们的文件较小,这里作为叶子节点直接指向了文件数据块。数据块号为0x001c4ad6=1854166。我们使用命令查看块中的内容:

[root@localhost Desktop]# blkcat /dev/mapper/VolGroup-lv_root 1854166

#include <stdlib.h>

#include <math.h>

...

 

呵呵,可以看到,这就是我们的math.c文件。

 

思考:

请读者找一个大于4k的文件,看看能不能找到它的数据块。

 

删除文件

执行rm后删除文件,数据块并没有被清除,inode被释放,有下面3项会改变: 1. 文件大小被置为0

    2. 段头中的区段值被设为0

    3. 区段被清空

清空了区段意味着我们会失去文件起始物理块的地址和区段的长度。也就是说,在inode中已经不存在元数据可以帮我们恢复文件。这种行为和ext3回收inode时清除inode中的块指针很相似。这样就意味着我们只能靠传统的file-carving去恢复文件。

 

 

参考资料:

http://lxr.linux.no/linux+v2.6.32.60/fs/ext4/ext4.h

https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout

http://computer-forensics.sans.org/blog/2010/12/20/digital-forensics-understanding-ext4-part-1-extents

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 顺丰快递在预计时间没有回来怎么办 同款商品比京东便宜怎么办 京东自营不支持7天退货怎么办 天猫买了一个月的电动车坏了怎么办 发票号码和机打号码不一致怎么办 交电费的本子弄丢了怎么办 快递正在派件中发现地址错了怎么办 快递当天送达当天签收还算延怎么办 深圳国税公众号预约取号公司怎么办 社保买了停了2年怎么办 qq号被冻结申请不回来了怎么办 qq号被冻结 手机密保忘了怎么办 微信账号封了2天怎么办 买qq号被申诉找回了怎么办 收到了京东白条的催款通知单怎么办 成都买房社保不够两年怎么办18年 电话号码给人设置成骚扰电话怎么办 找不到领导电话不接短信不回怎么办 微信账号被别人手机号冻结了怎么办 微信冻结了手机号也让人换了怎么办 顺丰快递拒收退回丢件了怎么办 京东买东西快递电话没有听到怎么办 在京东购物自己电话号输错了怎么办 北京房子卖了户口没地方迁怎么办 微信弄丢了微信密码找不到了怎么办 微信背人用过找不到密码怎么办 超市的微信支付宝收付款怎么办 办理联华超市的会员储蓄卡怎么办 卡杰文具密码本如果忘记密码怎么办 火狐浏览器阻止要访问的网页怎么办 点我达被永久停用了怎么办 刚下的软件点开系统显示停用怎么办 红酒洋酒啤酒一起喝胃不舒服怎么办 儿子13岁初一不想读书了怎么办 微信不小心点了注册新账号怎么办 在京东买东西商家不发货怎么办 在京东买东西坏了商家不退货怎么办 苯扎氯铵溶液不小心喝了一口怎么办 苯扎氯铵溶液没有稀释就用了怎么办 牛油果切开了但是没熟怎么办 手机安装程序时解析包出错怎么办