Linux内核-EXT文件系统结构及基本原理

1. 磁盘布局及文件系统结构

一般磁盘经过分区后,由MBR + MBR GAP + 若干分区组成.

a. MBR+MBR GAP一般是2048bytes, 主要用于写入引导程序(如grub、LILO等),引导系统启动;

b. MBR固定是512字节,446(引导写入区域)+64(分区表)+2(固定55aa)

分区进行格式化后,在分区的开头会预留空间作为Boot sector(一般1024bytes),剩下的空间切成若干个块组,块组的构成见下图.

块组各部分功能描述

超级块(Super block)

a. 超级块用于描述文件系统的基本信息,如起始位置、block和inode的数量及大小、文件系统支持的特性等.

b. 超级块对于文件系统是至关重要的,一般位于块组0的第一个block中,若干备份存在其他块组中,超级块损坏,会导致文件系统无法识别.

c. 超级块示例如下

Filesystem volume name:   <none>
Last mounted on:          /root
Filesystem UUID:          c55383bd-6336-4503-a8c8-a644f340bc4c
Filesystem magic number:  0xEF53
Filesystem revision #:    1 (dynamic)
Filesystem features:      has_journal ext_attr resize_inode dir_index filetype needs_recovery extent 64bit flex_bg sparse_super large_file huge_file dir_nlink extra_isize metadata_csum
Filesystem flags:         signed_directory_hash 
Default mount options:    user_xattr acl
Filesystem state:         clean
Errors behavior:          Continue
Filesystem OS type:       Linux
Inode count:              6430720
Block count:              25700608
Reserved block count:     1285029
Free blocks:              7497348
Free inodes:              5878745
First block:              0
Block size:               4096
Fragment size:            4096
Group descriptor size:    64
Reserved GDT blocks:      1018
Blocks per group:         32768
Fragments per group:      32768
Inodes per group:         8192
Inode blocks per group:   512
Flex block group size:    16
Filesystem created:       Thu Feb 18 10:40:42 2021
Last mount time:          Wed Mar 23 19:12:09 2022
Last write time:          Wed Mar 23 19:12:09 2022
Mount count:              172
Maximum mount count:      -1
Last checked:             Mon Nov  8 14:04:12 2021
Check interval:           0 (<none>)
Lifetime writes:          3056 GB
Reserved blocks uid:      0 (user root)
Reserved blocks gid:      0 (group root)
First inode:              11
Inode size:           256
Required extra isize:     32
Desired extra isize:      32
Journal inode:            8
First orphan inode:       1049286
Default directory hash:   half_md4
Directory Hash Seed:      11a0c299-a897-4d08-a36e-5d72df2a9581
Journal backup:           inode blocks
Checksum type:            crc32c
Checksum:                 0xe080d5c7
Journal features:         journal_incompat_revoke journal_64bit journal_checksum_v3
Journal size:             256M
Journal length:           65536
Journal sequence:         0x013ce92a
Journal start:            48115
Journal checksum type:    crc32c
Journal checksum:         0xf282bb35

块组描述表(GDT)

主要用于描述每个块组的起始位置,块组内超级块、块组描述表、inode表、inode位图、数据块位图、数据块等具体位置,示例如下.

Group 0: (Blocks 1-8192) [ITABLE_ZEROED]
  Checksum 0x2777, unused inodes 2004
  主 superblock at 1, Group descriptors at 2-4
  保留的GDT块位于 5-260
  Block bitmap at 261 (+260), Inode bitmap at 277 (+276)
  Inode表位于 293-544 (+292)
  3854 free blocks, 2004 free inodes, 2 directories, 2004个未使用的inodes
  可用块数: 4339-8192
  可用inode数: 13-2016
Group 1: (Blocks 8193-16384) [INODE_UNINIT, ITABLE_ZEROED]
  Checksum 0x2c84, unused inodes 2016
  备份 superblock at 8193, Group descriptors at 8194-8196
  保留的GDT块位于 8197-8452
  Block bitmap at 262 (bg #0 + 261), Inode bitmap at 278 (bg #0 + 277)
  Inode表位于 545-796 (bg #0 + 544)
  7932 free blocks, 2016 free inodes, 0 directories, 2016个未使用的inodes
  可用块数: 8453-16384
  可用inode数: 2017-4032
Group 2: (Blocks 16385-24576) [INODE_UNINIT, ITABLE_ZEROED]
  Checksum 0x2336, unused inodes 2016
  Block bitmap at 263 (bg #0 + 262), Inode bitmap at 279 (bg #0 + 278)
  Inode表位于 797-1048 (bg #0 + 796)
  8192 free blocks, 2016 free inodes, 0 directories, 2016个未使用的inodes
  可用块数: 16385-24576
  可用inode数: 4033-6048
Group 3: (Blocks 24577-32768) [INODE_UNINIT, ITABLE_ZEROED]
  Checksum 0x5ca1, unused inodes 2016
  备份 superblock at 24577, Group descriptors at 24578-24580
  保留的GDT块位于 24581-24836
  Block bitmap at 264 (bg #0 + 263), Inode bitmap at 280 (bg #0 + 279)
  Inode表位于 1049-1300 (bg #0 + 1048)
  7932 free blocks, 2016 free inodes, 0 directories, 2016个未使用的inodes
  可用块数: 24837-32768
  可用inode数: 6049-8064

块位图(Block bitmap)

数据块使用情况对照表,1bit对应一个数据块.

inode位图(inode bitmap)

inode块使用情况对照表,1bit对应一个数据块.

inode table

inode是文件索引项,每个文件对应一个inode,inode中记录了文件的基本属性及关联的数据块,inode数据结构如下.

struct ext4_inode {
    __le16  i_mode;     /* File mode */
    __le16  i_uid;      /* Low 16 bits of Owner Uid */
    __le32  i_size_lo;  /* Size in bytes */
    __le32  i_atime;    /* Access time */
    __le32  i_ctime;    /* Inode Change time */
    __le32  i_mtime;    /* Modification time */
    __le32  i_dtime;    /* Deletion Time */
    __le16  i_gid;      /* Low 16 bits of Group Id */
    __le16  i_links_count;  /* Links count */
    __le32  i_blocks_lo;    /* Blocks count */
    __le32  i_flags;    /* File flags */
    union {
        struct {
            __le32  l_i_version;
        } linux1;
        struct {
            __u32  h_i_translator;
        } hurd1;
        struct {
            __u32  m_i_reserved1;
        } masix1;
    } osd1;             /* OS dependent 1 */
    __le32  i_block[EXT4_N_BLOCKS];/* Pointers to blocks */
    __le32  i_generation;   /* File version (for NFS) */
    __le32  i_file_acl_lo;  /* File ACL */
    __le32  i_size_high;
    __le32  i_obso_faddr;   /* Obsoleted fragment address */
    union {
        struct {
            __le16  l_i_blocks_high; /* were l_i_reserved1 */
            __le16  l_i_file_acl_high;
            __le16  l_i_uid_high;   /* these 2 fields */
            __le16  l_i_gid_high;   /* were reserved2[0] */
            __le16  l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */
            __le16  l_i_reserved;
        } linux2;
        struct {
            __le16  h_i_reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */
            __u16   h_i_mode_high;
            __u16   h_i_uid_high;
            __u16   h_i_gid_high;
            __u32   h_i_author;
        } hurd2;
        struct {
            __le16  h_i_reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */
            __le16  m_i_file_acl_high;
            __u32   m_i_reserved2[2];
        } masix2;
    } osd2;             /* OS dependent 2 */
    __le16  i_extra_isize;
    __le16  i_checksum_hi;  /* crc32c(uuid+inum+inode) BE */
    __le32  i_ctime_extra;  /* extra Change time      (nsec << 2 | epoch) */
    __le32  i_mtime_extra;  /* extra Modification time(nsec << 2 | epoch) */
    __le32  i_atime_extra;  /* extra Access time      (nsec << 2 | epoch) */
    __le32  i_crtime;       /* File Creation time */
    __le32  i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */
    __le32  i_version_hi;   /* high 32 bits for 64-bit version */
    __le32  i_projid;   /* Project ID */
};

数据块(data block)

用于存放数据的block,每个block固定大小和唯一编号.

文件系统运行基本原理

普通文件与目录区别

普通文件与目录,都是文件,都有唯一的inode作为指向索引及数据块记录其内容. 两者的区别主要在内容上,目录的数据块记录的是一张表,这张表描述目录下所有文件文件名和inode的一一对应关系.

文件创建过程(顺序不一定对)

a. 分配inode x和block y给新文件,x指向y

b. 更新inode和block位图

c. 在新文件父目录的数据块中,更新文件名-inode对照表,增加一条记录

文件读取过程

文件读取,是从根目录开始,一层一层往下查找的,以/etc/fstab为例如下:

a. 读取/的inode(根目录的inode固定,一般为2),找到/的block,进行读取,找到etc的inode:

b. 读取etc的inode后,找到etc/的block,读取获得fstab的inode

c. 读取fstab的inode,获得fstab的block

d. 再读取fstab的block,即获取fstab内容

【文章福利】小编推荐自己的Linux内核技术交流群: 【977878001】整理一些个人觉得比较好得学习书籍、视频资料;进群私聊群管理领取 内核资料包(含视频教程、电子书、实战项目及代码)

内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料

学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈

2. ext3/4 JDB日志系统介绍

JDB日志系统是ext3在ext2的基础上增加的功能.

2.1 JDB日志系统功能阐述

假设系统运行在ext2分区上,正在读写磁盘. 突然掉电,或系统崩溃,你不得不强制重启系统,然而此时还有数据在内存缓冲区未写入磁盘;重启系统后,你可能会发现部分数据丢失,甚至文件系统元数据丢失,文件系统不完整一致,分区无法挂载等;调用fsck可能会修复分区,但会耗费大量时间. JDB日志系统主要是为应对此类情况而设计的,但JDB不能减少系统崩溃的概率,它主要解决的问题是:a. 系统出现异常重启时,尽量保持文件系统的完整和一致性(完整和一致性指文件系统元数据如超级块、块组描述表、块位图、inode位图、inode表及数据块,互相之间的对照关系是准确的,比如块位图和数据块实际被占用的情况一致,inode表和数据块的映射关系是准确的等);b. 文件系统损坏后,可修复的情况下,减少修复的耗时,主要是利用日志记录进行修复.

2.2 JDB运行基本原理

a. 定义原子操作

修改文件系统的任一系统调用都通常划分为操纵磁盘数据结构的一系列低级操作. 原子操作是单个低级操作或若干低级操作的组合,是内部不再分割的操作,该操作要么完全完成,要么根本没有执行,不存在部分完成的状态。比如说为文件分配一个磁盘块,可以看成一个原子操作。分配一个磁盘块,可能需要修改一个inode块、一个磁盘块位图、最多三个间接索引块、块组块、超级块,一共最多7个磁盘块。将分配一个磁盘块看成一个原子操作,意味着上述修改7个磁盘块的操作要么都成功,要么都失败,不可能有第三种状态。

b. 一系列原子操作组合成一个事务

实现日志文件系统时,可以将一个原子操作就作为一个事务来处理,但是这样实现的效率比较低。若干个原子操作组合成一个事务,对磁盘日志以事务为单位进行管理,以提高读写日志的效率.

c. 在磁盘上划分空间存储事务日志

将原子操作组成的事务,写到日志空间上,这部分日志即为磁盘数据操作的历史记录,利用这部分数据回溯,可实现数据还原.

d. 通过事务状态跟踪事务完成情况

事务运行会经历下面的一系列状态:

运行(running):事务当前在内存中,还可以接受新的原子操作。在一个系统中,仅有一个事务可以处于运行状态

锁定(locked):事务不再接受新的原子操作,但现有原子操作们还没有完成。一旦所有原子操作都完成了,事务将进入下一个状态

写入(flush):事务中的所有原子操作都完成了,事务正在写入日志

提交(commit):事务已写入日志。事务会写一个提交块,指示事务log已写入日志

完成(Finished):事务写到日志之后,它会留在那直到所有的块都被更新到磁盘上的实际位置

2.3 JDB事务日志结构

从上图可以看到,JDB日志有超级块、描述块、数据块、提交块及取消块组成.

a. 超级块(JFS_SUPERBLOCK):日志中超级块起的作用与文件系统中超级块的作用是类似的,都是用于组织管理一段磁盘空间.

b. 描述块(JFS_DESCRIPTOR_BLOCK):一个事务以描述块开始,以提交块结束. 描述块主要作用是描述本事务中的日志块,记录的是哪个磁盘块的操作记录.

c. 数据块:记录磁盘块的数据操作

d. 提交块(JFS_COMMIT_BLOCK):提交块表明一个事务的完成.

e. 取消块(JFS_REVOKE_BLOCK):事务中包含删除磁盘块操作时,就会在日志中写一个取消块,表明取消块之前,对应磁盘块的操作都可以忽略.

通过debugfs查看JDB日志

debugfs:  logdump -a
Journal starts at block 1, transaction 51
Found expected sequence 51, type 1 (descriptor block) at block 1
Dumping descriptor block, sequence 51, at block 1:
  FS block 293 logged at journal block 2 (flags 0x0)
  FS block 277 logged at journal block 3 (flags 0x2)
  FS block 2 logged at journal block 4 (flags 0x2)
  FS block 294 logged at journal block 5 (flags 0x2)
  FS block 4325 logged at journal block 6 (flags 0x2)
  FS block 1 logged at journal block 7 (flags 0xa)
Found expected sequence 51, type 2 (commit block) at block 8
Found expected sequence 52, type 1 (descriptor block) at block 9
Dumping descriptor block, sequence 52, at block 9:
  FS block 294 logged at journal block 10 (flags 0x8)
Found expected sequence 52, type 2 (commit block) at block 11
Found expected sequence 53, type 1 (descriptor block) at block 12
Dumping descriptor block, sequence 53, at block 12:
  FS block 277 logged at journal block 13 (flags 0x0)
  FS block 2 logged at journal block 14 (flags 0x2)
  FS block 294 logged at journal block 15 (flags 0x2)
  FS block 293 logged at journal block 16 (flags 0x2)
  FS block 4325 logged at journal block 17 (flags 0x2)
  FS block 262 logged at journal block 18 (flags 0xa)
Found expected sequence 53, type 2 (commit block) at block 19
Found expected sequence 54, type 1 (descriptor block) at block 20
Dumping descriptor block, sequence 54, at block 20:
  FS block 294 logged at journal block 21 (flags 0x0)
  FS block 4325 logged at journal block 22 (flags 0x2)
  FS block 293 logged at journal block 23 (flags 0x2)
  FS block 1 logged at journal block 24 (flags 0x2)
  FS block 2 logged at journal block 25 (flags 0x2)
  FS block 277 logged at journal block 26 (flags 0x2)
  FS block 131105 logged at journal block 27 (flags 0xa)
Found expected sequence 54, type 2 (commit block) at block 28
Found expected sequence 55, type 1 (descriptor block) at block 29
Dumping descriptor block, sequence 55, at block 29:
  FS block 135137 logged at journal block 30 (flags 0x0)
  FS block 131105 logged at journal block 31 (flags 0x2)
  FS block 1 logged at journal block 32 (flags 0x2)
  FS block 131075 logged at journal block 33 (flags 0x2)
  FS block 3 logged at journal block 34 (flags 0x2)
  FS block 131089 logged at journal block 35 (flags 0xa)
Found expected sequence 55, type 2 (commit block) at block 36
Found expected sequence 56, type 1 (descriptor block) at block 37
Dumping descriptor block, sequence 56, at block 37:
  FS block 131105 logged at journal block 38 (flags 0x0)
  FS block 293 logged at journal block 39 (flags 0x2)
  FS block 131089 logged at journal block 40 (flags 0x2)
  FS block 3 logged at journal block 41 (flags 0x2)
  FS block 135138 logged at journal block 42 (flags 0x2)
  FS block 1 logged at journal block 43 (flags 0xa)
Found expected sequence 56, type 2 (commit block) at block 44
Found expected sequence 57, type 1 (descriptor block) at block 45
Dumping descriptor block, sequence 57, at block 45:
  FS block 131105 logged at journal block 46 (flags 0x0)
  FS block 131089 logged at journal block 47 (flags 0x2)
  FS block 3 logged at journal block 48 (flags 0x2)
  FS block 135138 logged at journal block 49 (flags 0xa)
Found expected sequence 57, type 2 (commit block) at block 50
Found expected sequence 58, type 1 (descriptor block) at block 51
Dumping descriptor block, sequence 58, at block 51:
  FS block 262 logged at journal block 52 (flags 0x0)
  FS block 2 logged at journal block 53 (flags 0x2)
  FS block 131105 logged at journal block 54 (flags 0xa)
Found expected sequence 58, type 2 (commit block) at block 55
Found expected sequence 59, type 1 (descriptor block) at block 56
Dumping descriptor block, sequence 59, at block 56:
  FS block 131105 logged at journal block 57 (flags 0x0)
  FS block 135138 logged at journal block 58 (flags 0x2)
  FS block 1 logged at journal block 59 (flags 0x2)
  FS block 3 logged at journal block 60 (flags 0x2)
  FS block 131089 logged at journal block 61 (flags 0xa)
Found expected sequence 59, type 2 (commit block) at block 62
Found sequence 36 (not 60) at block 63: end of journal.

3. ext2/3/4文件系统调试工具介绍

mke2fs :用于创建ext文件系统

dumpe2fs :查看文件系统超级块和块组描述表

tune2fs :用于调整文件系统参数

e2fsck :检查和修复文件系统

debugfs :文件系统debug工具,功能强大,可以用来查看JDB日志

extundelete :利用JDB日志,修复被删除的文件

badblocks :检查磁道坏块

4. 关于文件系统&数据的恢复的思考

a. 对于异常被删除的数据,第一时间卸载磁盘,避免新的数据覆盖旧数据,然后通过对JDB日志分析,有可能能恢复数据,extundelete工具就是利用的这个原理.

b. 文件系统损坏的话,可尝试通过fsck、e2fsck工具进行修复,fsck会全盘检查,效率较低;e2fsck利用JDB日志,修复效率会更高.

c. 文件系统结构破坏的情况下,是否有机会恢复数据?

这种情况是最让人头疼的,也是最复杂的,仅从理论上分析,应该是有可能的,主要考虑以下几个方面的问题:

i:文件系统的目录结构存在于inode table和数据块中,如果能定位到这2部分的位置,应该就能恢复大部分数据.

ii:关于inode table的位置定位,inode块通常是连续且大小固定的,inode又是统一固定的数据结构,猜测通过特征比对,应该能识别出来,定位到连续的inode块,也就能定位到inode位置.

iii:数据块的起始位置就在inode table的结束位置.

iiii:还有就是要确定数据块的编号,数据块的编号定位不准确的话,获取的内容错误的,并且会导致连锁反应的错误,所以必须要精确.

5. 参考文献

Ext3文件系统及JDB介绍

journal block device jbd 源代码分析

6. 遗留

文件系统结构被破坏,文件系统和数据的恢复手段

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值