Skip to the content.

ext4 文档

内部文档

https://docs.kernel.org/filesystems/ext4/

文件数据组织方式: Extent 树:这是 ext4 的主要特性,用以替代 ext2/3 的间接块映射。它通过树形结构高效地记录文件数据块的连续范围,极大减少了大文件的元数据开销。 传统块映射 (Direct/Indirect Block Addressing):为了向后兼容,文档也解释了旧的三级间接块寻址方式。 目录结构 (Directory Entries):详细描述了目录如何存储为特殊文件,包含线性目录和更高效的哈希树 (htree) 目录两种格式。

高级特性和优化:

日志 (Journal, jbd2):深入剖析了 ext4 用于保证元数据一致性的日志系统,包括其磁盘布局、事务结构(描述符块、数据块、提交块、撤销块)、外部日志以及新的“快速提交 (Fast Commit)”功能。 特殊 inode:列出了保留给特定功能的 inode 编号,如根目录 (2)、日志 (8)、配额文件等。

inode table

Inode tables are statically allocated at mkfs time. Each block group descriptor points to the start of the table, and the superblock records the number of inodes per group. See the section on inodes for more information.

应该是一个 block group ,就有一个 inode table ,inode table 是静态分配的。

ext4 : extend 树

树的结构如下:

/*
 * Each block (leaves and indexes), even inode-stored has header.
 */
struct ext4_extent_header {
	__le16	eh_magic;	/* probably will support different formats */
	__le16	eh_entries;	/* number of valid entries */
	__le16	eh_max;		/* capacity of store in entries */
	__le16	eh_depth;	/* has tree real underlying blocks? */
	__le32	eh_generation;	/* generation of the tree */
};
/*
 * This is the extent on-disk structure.
 * It's used at the bottom of the tree.
 */
struct ext4_extent {
	__le32	ee_block;	/* first logical block extent covers */
	__le16	ee_len;		/* number of blocks covered by extent */
	__le16	ee_start_hi;	/* high 16 bits of physical block */
	__le32	ee_start_lo;	/* low 32 bits of physical block */
};

/*
 * This is index on-disk structure.
 * It's used at all the levels except the bottom.
 */
struct ext4_extent_idx {
	__le32	ei_block;	/* index covers logical blocks from 'block' */
	__le32	ei_leaf_lo;	/* pointer to the physical block of the next *
				 * level. leaf or next index could be there */
	__le16	ei_leaf_hi;	/* high 16 bits of physical block */
	__u16	ei_unused;
};

ext4_extent 中的 logical 就文件中偏移量,而 physical block 就是在盘中偏移量

struct ext4_extent_idx (内部节点条目):

高级特性:

ext4 : 哈希树 (htree) 目录

  1. 根节点 (Root Node):
    struct dx_root
    {
     struct fake_dirent dot;
     char dot_name[4];
     struct fake_dirent dotdot;
     char dotdot_name[4];
     struct dx_root_info
     {
         __le32 reserved_zero;
         u8 hash_version;
         u8 info_length; /* 8 */
         u8 indirect_levels;
         u8 unused_flags;
     }
     info;
     struct dx_entry	entries[];
    };
    
  1. 内部节点 (Interior Nodes):
    struct dx_node
    {
     struct fake_dirent fake;
     struct dx_entry	entries[];
    };
    
  1. 树的最底层。
    /*
     * The new version of the directory entry.  Since EXT4 structures are
     * stored in intel byte order, and the name_len field could never be
     * bigger than 255 chars, it's safe to reclaim the extra byte for the
     * file_type field.
     */
    struct ext4_dir_entry_2 {
     __le32	inode;			/* Inode number */
     __le16	rec_len;		/* Directory entry length */
     __u8	name_len;		/* Name length */
     __u8	file_type;		/* See file type macros EXT4_FT_* below */
     char	name[EXT4_NAME_LEN];	/* File name */
    };
    

htree 的一个精妙之处在于它的向后兼容性。

bigalloc

类似文件系统中 hugetlb 了:

mkfs.ext4 -F -O bigalloc -b 4096 -C 65536 /dev/nvme1n1 sudo dumpe2fs -h /dev/nvme1n1

可以看到 :

Cluster size:             65536

Atomic Block Writes

忽然意识到,没有操作元数据,就不要和 journal 打交道了

jbd2

在将任何脏的元数据块写回到它们在文件系统中的“最终位置”(home location)之前,jbd2 会先将这些块的副本(或描述其变更的信息)按顺序写入到一个连续的、专用的日志区域(journal)。

只有当整个事务的所有块都安全地写入日志并被“提交”(commit)后,jbd2 才会在后台异步地将这些块写回到它们原本在文件系统中的位置。

崩溃恢复的关键:如果系统在“写日志”和“写回原位置”之间崩溃,重启后,文件系统只需重放(replay)日志中已提交的事务,就能将文件系统恢复到崩溃前的一致状态。那些未提交的事务会被直接丢弃。

高性能与低延迟:

所以 jdb 的运行过程为: 我们可以更精确地阐述这个过程:

@[
    jbd2_journal_dirty_metadata+0
    ext4_mark_iloc_dirty+336
    __ext4_mark_inode_dirty+212
    ext4_dirty_inode+108
    __mark_inode_dirty+160
    generic_update_time+88
    file_modified+188
    ext4_buffered_write_iter+96
    ext4_file_write_iter+156
    vfs_write+548
    __arm64_sys_pwrite64+176
    invoke_syscall.constprop.0+88
    do_el0_svc+72
    el0_svc+92
    el0t_64_sync_handler+268
    el0t_64_sync+408
]: 62

@[
    jbd2_journal_get_write_access+0
    ext4_reserve_inode_write+180
    __ext4_mark_inode_dirty+172
    ext4_da_write_end+932
    generic_perform_write+316
    ext4_buffered_write_iter+116
    ext4_file_write_iter+156
    vfs_write+548
    __arm64_sys_pwrite64+176
    invoke_syscall.constprop.0+88
    do_el0_svc+72
    el0_svc+92
    el0t_64_sync_handler+268
    el0t_64_sync+408
]: 390

当完成提交之后

@[
    jbd2_journal_stop+0
    ext4_da_write_end+948
    generic_perform_write+316
    ext4_buffered_write_iter+116
    ext4_file_write_iter+156
    vfs_write+548
    __arm64_sys_pwrite64+176
    invoke_syscall.constprop.0+88
    do_el0_svc+72
    el0_svc+92
    el0t_64_sync_handler+268
    el0t_64_sync+408
]: 809

真正的提交在另外一个 thread 中:

@[
    jbd2_journal_commit_transaction+0
    kthread+340
    ret_from_fork+16
]: 11

问题:

TODO

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。