1. ext4磁盘结构
块组:
超级块:
块位图:
inode位图:
inode表:
空闲inode表:
空闲块表:
2. 块组结构
Group 0: (Blocks 0-32767) csum 0xfd42 [ITABLE_ZEROED]
Primary superblock at 0, Group descriptors at 1-125
Reserved GDT blocks at 126-1149
Block bitmap at 1150 (+1150), csum 0xe5f8ae61
Inode bitmap at 1166 (+1166), csum 0xf88cceaa
Inode table at 1182-1693 (+1182)
23388 free blocks, 8181 free inodes, 2 directories, 8181 unused inodes
Free blocks: 9380-32767
Free inodes: 12-8192
Group 0: 这是文件系统的第一个块组,通常包含文件系统的元数据,如超级块、块组描述符、块位图和inode位图。
Blocks 0-32767: 这个块组包含的块范围是从0到32767。
csum 0xfd42 [ITABLE_ZEROED]: 这是块组的校验和,用于检测数据的完整性。ITABLE_ZEROED 表示inode表是清零的。
Primary superblock at 0: 主超级块位于块组的第0块。
Group descriptors at 1-125: 块组描述符位于第1到第125块。
Reserved GDT blocks at 126-1149: 保留的GDT(Group Descriptor Table)块位于第126到第1149块。
Block bitmap at 1150 (+1150), csum 0xe5f8ae61: 块位图位于第1150块,校验和为0xe5f8ae61。块位图用于标记哪些块被使用,哪些是空闲的。
Inode bitmap at 1166 (+1166), csum 0xf88cceaa: inode位图位于第1166块,校验和为0xf88cceaa。inode位图用于标记哪些inode被使用,哪些是空闲的。
Inode table at 1182-1693 (+1182): inode表位于第1182到第1693块。
23388 free blocks, 8181 free inodes, 2 directories, 8181 unused inodes: 这个块组中有23388个空闲块和8181个空闲inode。还有2个目录和8181个未使用的inode。
Free blocks: 9380-32767: 空闲块的范围是从9380到32767。
Free inodes: 12-8192: 空闲inode的范围是从12到8192。
查看对应的inode 位图:
块位图位于第1150块,假设块大小为4096字节,你可以使用以下命令:
bash
hexdump -C /dev/sdX -s $((1150 * 4096)) -n 4096
3. ext4块组描述图
struct ext4_group_desc
{
__le32 bg_block_bitmap_lo; /* Blocks bitmap block */
__le32 bg_inode_bitmap_lo; /* Inodes bitmap block */
__le32 bg_inode_table_lo; /* Inodes table block */
__le16 bg_free_blocks_count_lo;/* Free blocks count */
__le16 bg_free_inodes_count_lo;/* Free inodes count */
__le16 bg_used_dirs_count_lo; /* Directories count */
__le16 bg_flags; /* EXT4_BG_flags (INODE_UNINIT, etc) */
__le32 bg_exclude_bitmap_lo; /* Exclude bitmap for snapshots */
__le16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bbitmap) LE */
__le16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+ibitmap) LE */
__le16 bg_itable_unused_lo; /* Unused inodes count */
__le16 bg_checksum; /* crc16(sb_uuid+group+desc) */
__le32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */
__le32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */
__le32 bg_inode_table_hi; /* Inodes table block MSB */
__le16 bg_free_blocks_count_hi;/* Free blocks count MSB */
__le16 bg_free_inodes_count_hi;/* Free inodes count MSB */
__le16 bg_used_dirs_count_hi; /* Directories count MSB */
__le16 bg_itable_unused_hi; /* Unused inodes count MSB */
__le32 bg_exclude_bitmap_hi; /* Exclude bitmap block MSB */
__le16 bg_block_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bbitmap) BE */
__le16 bg_inode_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+ibitmap) BE */
__u32 bg_reserved;
};
bg_block_bitmap_lo:块位图的低32位地址。块位图用于追踪块组内哪些块被使用,哪些是空闲的。
bg_inode_bitmap_lo:inode位图的低32位地址。inode位图用于追踪块组内哪些inode被使用,哪些是空闲的。
bg_inode_table_lo:inode表的低32位地址。inode表包含了块组内所有inode的记录。
bg_free_blocks_count_lo:块组内空闲块数量的低16位。这个数字表示当前块组中未使用的块的数量。
bg_free_inodes_count_lo:块组内空闲inode数量的低16位。这个数字表示当前块组中未使用的inode的数量。
bg_used_dirs_count_lo:块组内已使用的目录数量的低16位。这个数字表示当前块组中已创建的目录数量。
bg_flags:块组标志。用于指示块组的特殊属性,如是否包含未初始化的inode。
bg_exclude_bitmap_lo:快照排除位图的低32位地址。用于追踪块组内哪些块被排除在快照之外。
bg_block_bitmap_csum_lo:块位图的CRC校验和的低16位。用于校验块位图的完整性。
bg_inode_bitmap_csum_lo:inode位图的CRC校验和的低16位。用于校验inode位图的完整性。
bg_itable_unused_lo:未使用inode数量的低16位。表示块组内未使用的inode数量。
bg_checksum:块组描述符的CRC校验和。用于校验整个块组描述符的完整性。
bg_block_bitmap_hi:块位图的高32位地址(64位系统)。
bg_inode_bitmap_hi:inode位图的高32位地址(64位系统)。
bg_inode_table_hi:inode表的高32位地址(64位系统)。
bg_free_blocks_count_hi:空闲块数量的高16位(64位系统)。
bg_free_inodes_count_hi:空闲inode数量的高16位(64位系统)。
bg_used_dirs_count_hi:已使用的目录数量的高16位(64位系统)。
bg_itable_unused_hi:未使用inode数量的高16位(64位系统)。
bg_exclude_bitmap_hi:快照排除位图的高32位地址(64位系统)。
bg_block_bitmap_csum_hi:块位图的CRC校验和的高16位(大端序)。
bg_inode_bitmap_csum_hi:inode位图的CRC校验和的高16位(大端序)。
bg_reserved:保留的字段,用于未来扩展。
如上图,通过块组位置和块组描述符,我们找到了block bitmap 对应的位置 0x0610 == 1537.
4.超级块
/*
* Structure of the super block
*/
struct ext4_super_block {
/*00*/ __le32 s_inodes_count; /* Inodes count */
__le32 s_blocks_count_lo; /* Blocks count */
__le32 s_r_blocks_count_lo; /* Reserved blocks count */
__le32 s_free_blocks_count_lo; /* Free blocks count */
/*10*/ __le32 s_free_inodes_count; /* Free inodes count */
__le32 s_first_data_block; /* First Data Block */
__le32 s_log_block_size; /* Block size */
__le32 s_log_cluster_size; /* Allocation cluster size */
/*20*/ __le32 s_blocks_per_group; /* # Blocks per group */
__le32 s_clusters_per_group; /* # Clusters per group */
__le32 s_inodes_per_group; /* # Inodes per group */
__le32 s_mtime; /* Mount time */
/*30*/ __le32 s_wtime; /* Write time */
__le16 s_mnt_count; /* Mount count */
__le16 s_max_mnt_count; /* Maximal mount count */
__le16 s_magic; /* Magic signature */
__le16 s_state; /* File system state */
__le16 s_errors; /* Behaviour when detecting errors */
__le16 s_minor_rev_level; /* minor revision level */
/*40*/ __le32 s_lastcheck; /* time of last check */
__le32 s_checkinterval; /* max. time between checks */
__le32 s_creator_os; /* OS */
__le32 s_rev_level; /* Revision level */
/*50*/ __le16 s_def_resuid; /* Default uid for reserved blocks */
__le16 s_def_resgid; /* Default gid for reserved blocks */
/*
* These fields are for EXT4_DYNAMIC_REV superblocks only.
*
* Note: the difference between the compatible feature set and
* the incompatible feature set is that if there is a bit set
* in the incompatible feature set that the kernel doesn't
* know about, it should refuse to mount the filesystem.
*
* e2fsck's requirements are more strict; if it doesn't know
* about a feature in either the compatible or incompatible
* feature set, it must abort and not try to meddle with
* things it doesn't understand...
*/
__le32 s_first_ino; /* First non-reserved inode */
__le16 s_inode_size; /* size of inode structure */
__le16 s_block_group_nr; /* block group # of this superblock */
__le32 s_feature_compat; /* compatible feature set */
/*60*/ __le32 s_feature_incompat; /* incompatible feature set */
__le32 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] __nonstring; /* directory where last mounted */
/*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */
/*
* Performance hints. Directory preallocation should only
* happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on.
*/
__u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/
__u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
__le16 s_reserved_gdt_blocks; /* Per group desc for online growth */
/*
* Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set.
*/
/*D0*/ __u8 s_journal_uuid[16]; /* uuid of journal superblock */
/*E0*/ __le32 s_journal_inum; /* inode number of journal file */
__le32 s_journal_dev; /* device number of journal file */
__le32 s_last_orphan; /* start of list of inodes to delete */
__le32 s_hash_seed[4]; /* HTREE hash seed */
__u8 s_def_hash_version; /* Default hash version to use */
__u8 s_jnl_backup_type;
__le16 s_desc_size; /* size of group descriptor */
/*100*/ __le32 s_default_mount_opts;
__le32 s_first_meta_bg; /* First metablock block group */
__le32 s_mkfs_time; /* When the filesystem was created */
__le32 s_jnl_blocks[17]; /* Backup of the journal inode */
/* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */
/*150*/ __le32 s_blocks_count_hi; /* Blocks count */
__le32 s_r_blocks_count_hi; /* Reserved blocks count */
__le32 s_free_blocks_count_hi; /* Free blocks count */
__le16 s_min_extra_isize; /* All inodes have at least # bytes */
__le16 s_want_extra_isize; /* New inodes should reserve # bytes */
__le32 s_flags; /* Miscellaneous flags */
__le16 s_raid_stride; /* RAID stride */
__le16 s_mmp_update_interval; /* # seconds to wait in MMP checking */
__le64 s_mmp_block; /* Block for multi-mount protection */
__le32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/
__u8 s_log_groups_per_flex; /* FLEX_BG group size */
__u8 s_checksum_type; /* metadata checksum algorithm used */
__u8 s_encryption_level; /* versioning level for encryption */
__u8 s_reserved_pad; /* Padding to next 32bits */
__le64 s_kbytes_written; /* nr of lifetime kilobytes written */
__le32 s_snapshot_inum; /* Inode number of active snapshot */
__le32 s_snapshot_id; /* sequential ID of active snapshot */
__le64 s_snapshot_r_blocks_count; /* reserved blocks for active
snapshot's future use */
__le32 s_snapshot_list; /* inode number of the head of the
on-disk snapshot list */
#define EXT4_S_ERR_START offsetof(struct ext4_super_block, s_error_count)
__le32 s_error_count; /* number of fs errors */
__le32 s_first_error_time; /* first time an error happened */
__le32 s_first_error_ino; /* inode involved in first error */
__le64 s_first_error_block; /* block involved of first error */
__u8 s_first_error_func[32] __nonstring; /* function where the error happened */
__le32 s_first_error_line; /* line number where error happened */
__le32 s_last_error_time; /* most recent time of an error */
__le32 s_last_error_ino; /* inode involved in last error */
__le32 s_last_error_line; /* line number where error happened */
__le64 s_last_error_block; /* block involved of last error */
__u8 s_last_error_func[32] __nonstring; /* function where the error happened */
#define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts)
__u8 s_mount_opts[64];
__le32 s_usr_quota_inum; /* inode for tracking user quota */
__le32 s_grp_quota_inum; /* inode for tracking group quota */
__le32 s_overhead_clusters; /* overhead blocks/clusters in fs */
__le32 s_backup_bgs[2]; /* groups with sparse_super2 SBs */
__u8 s_encrypt_algos[4]; /* Encryption algorithms in use */
__u8 s_encrypt_pw_salt[16]; /* Salt used for string2key algorithm */
__le32 s_lpf_ino; /* Location of the lost+found inode */
__le32 s_prj_quota_inum; /* inode for tracking project quota */
__le32 s_checksum_seed; /* crc32c(uuid) if csum_seed set */
__u8 s_wtime_hi;
__u8 s_mtime_hi;
__u8 s_mkfs_time_hi;
__u8 s_lastcheck_hi;
__u8 s_first_error_time_hi;
__u8 s_last_error_time_hi;
__u8 s_pad[2];
__le16 s_encoding; /* Filename charset encoding */
__le16 s_encoding_flags; /* Filename charset encoding flags */
__le32 s_reserved[95]; /* Padding to the end of the block */
__le32 s_checksum; /* crc32c(superblock) */
};
5.为什么第一个super_block 不是从偏移0的位置开始
当文件系统的块大小设置为4096字节时,超级块(ext4_super_block)从偏移量1024字节(1个扇区)开始的原因主要是为了向后兼容和对齐
。
向后兼容性:在早期的文件系统如EXT2中,块大小通常为1024字节,超级块位于第一个块的开始处。为了保持与旧版本文件系统的兼容性,EXT4文件系统保留了这种布局,即使块大小增加到4096字节
。
磁盘对齐:磁盘对齐是指文件系统的起始位置被设置为磁盘扇区大小的整数倍。随着硬盘扇区大小从512字节增加到4096字节(即4K扇区),为了提高磁盘读写效率和减少磁盘空间浪费,文件系统的起始位置通常设置为4096字节的整数倍
。因此,超级块从1024字节偏移开始,可以确保它位于第二个4096字节块的开始位置,实现4K对齐
。
性能优化:将超级块放在1024字节偏移处,可以为引导加载程序和分区表留出空间,同时确保文件系统的关键元数据(如超级块和组描述符)在磁盘上的分布是均匀的,这有助于提高文件系统的读写性能和可靠性
。
避免MBR限制:如果块大小为4096字节,超级块可能会与MBR(主引导记录)所在的区域冲突。MBR通常位于磁盘的第一个扇区(前512字节),因此将超级块从1024字节偏移开始,可以避免与MBR冲突
。