linux Ext4 文件系统简介
Ext 文件的发展史
- MINIX : 微机的一个非常小的Unix操作系统,最多能处理14个字符的文件名,只能处理 64MB 的存储空间
- EXT1: 第一个利用虚拟文件系统, 2GB存储空间并处理255个字符的文件
- EXT2: 商业级文件系统, GB级别的最大文件大小和TB级别的文件系统大小
- EXT3 : 增加日志功能
- EXT4:提供更佳的性能和可靠性,更大的文件系统和更大的文件、无限数量的子目录、Extents、多块分配、延迟分配,快速 fsck、日志校验,No Journaling模式
MBR分区和 GPT分区
- MBR的意思是“主引导记录”,是IBM公司早年间提出的。它是存在于磁盘驱动器开始部分的一个特殊的启动扇区。 这个扇区包含了引导程序(grub)和已安装的操作系统系统信息。
- GPT的意思是GUID Partition Table,即“全局唯一标识磁盘分区表”。它是另外一种更加先进新颖的磁盘组织方式, 一种使用UEFI启动的磁盘组织方式。因为其更大的支持内存(mbr分区最多支持2T的磁盘,GPT支持2T以上)
BIOS + MBR 启动系统
- BIOS下启动操作系统之前,必须从硬盘上指定扇区读取系统启动代码 (包含在MBR主引导记录中),然后从活动分区中引导启动操作系统,所以在BIOS下引导安装Windows 操作系统,我们不得不使用一些工具(DiskGenius)对硬盘进行配置以达到启动要求 (即建立MBR硬盘主引导和活动分区
UEFI + GPT 启动系统
- UEFI之所以比BIOS强大,是因为UEFI本身已经相当于一个微型操作系统,其带来的便利之处在于:首先,UEFI已具备文件系统 (文件系统是操作系统组织管理文件的一种方法,直白点说就是把硬盘上的数据以文件的形式呈现给用户。 Fat32、NTFS都是常见的文件系统类型)的支持,它能够直接读取FAT分区中的文件;其次, 可开发出直接在UEFI下运行的应用程序,这类程序文件通常以efi结尾
- U盘启动使用的就是UEFI的方式
启动linux系统步骤
- 开机之后,处理器进入实模式,实模式采用段式内存管理,并没有开启分页机制(CR3没有置位) 内存被分成固定的64k大小的块
- 然后开始跳转到BIOS的入口地址开始执行。BIOS执行初始化和硬件检查之后,开始寻找引导设备(程序) 如果存在MBR分区,则引导扇区储存在第一个扇区(512字节)的头446字节,引导扇区的最后必须是 0x55 和 0xaa ,这2个字节称为魔术字节(Magic Bytes),如果 BIOS 看到这2个字节,就知道这个设备是一个可引导设备
- BIOS把系统控制权移交给引导程序,引导程序运行在实模式下。
- 引导程序有GRUB2 和syslinux,这里介绍GRUB2.
- GRUB 置于 normal 模式,在这个模式中,grub_normal_execute (from grub-core/normal/main.c) 将被调用以完成最后的准备工作, 然后显示一个菜单列出所用可用的操作系统。当某个操作系统被选择之后,grub_menu_execute_entry 开始执行, 它将调用 GRUB 的 boot 命令,来引导被选中的操作系统。 就像 kernel boot protocol 所描述的,引导程序必须填充 kernel setup header (位于 kernel setup code 偏移 0x01f1 处) 的必要字段。kernel setup header的定义开始于 arch/x86/boot/header.S
- kernel的setup()函数初始化硬件设备和内核运行环境,设置CR0的PE位为0,从实模式切换到保护模式,跳转到startup_32,调用startup_32()函数继续一些初始化的工作
- startup_32()函数调用start_kernel(),该函数完成内核的初始化,到这里linux内核才起来开始运行。
Ext4的磁盘布局


- Ext4文件系统把整个分区划分成各个block group(块组)
- 1024 bytes 的 Group 0 Padding(boot block)只有 块组0 有,用于装载该分区的操作系统。
MBR 为主引导记录用来引导计算机。在计算机启动时,BIOS 读入并执行 MBR,MBR 作的第一件事
就是确定活动分区(这对应于双系统的计算机开机时选择启动项,单系统的直接就能确定了
所以就不需要选择),读入活动分区的引导块(Boot block),引导块再加载该分区中的操作系统

ext4_super_block 超级块
ext4_group_desc 组描述符
ext4_inode 索引节点-——> 索引节点在内存的数据结构ext4_inode_info
inode table 索引节点表是struct ext4_inode的线性数组
- 符号连接的路径名小于60B,存放在索引节点的i_blocks字段,大于60B就需要分配数据块
- 设备文件,管道,套接字等特殊文件不需要数据块,所有信息存储在索引节点。
- ext4_super_block数据结构是超级块在磁盘的存储模式,其在内存的数据结构 表现为ext4_sb_info(VFS超级块super_block是对物理磁盘块的描述),ext4_sb_info数据结构包含 整个ext4文件系统的信息。
- 超级块和组描述符长期驻留内存(高速缓存),
- VFS的相关操作函数都是通过注册为ext4处理函数的方式实现, 这样VFS就能屏蔽硬件,与硬件相关的各种文件系统的实现VFS都不关心,VFS只需要提供文件处理相关的接口, 相应的文件系统ext4,xfs,zfs等,只需要把文件处理函数注册到VFS,VFS就能实现多个文件系统的兼容。
ext4向VFS注册文件操作接口
const struct file_operations ext4_file_operations = {
.llseek = ext4_llseek,
.read_iter = ext4_file_read_iter,
.write_iter = ext4_file_write_iter,
.iopoll = iomap_dio_iopoll,
.unlocked_ioctl = ext4_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext4_compat_ioctl,
#endif
.mmap = ext4_file_mmap,
.mmap_supported_flags = MAP_SYNC,
.open = ext4_file_open,
.release = ext4_release_file,
.fsync = ext4_sync_file,
.get_unmapped_area = thp_get_unmapped_area,
.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
.fallocate = ext4_fallocate,
};
const struct inode_operations ext4_file_inode_operations = {
.setattr = ext4_setattr,
.getattr = ext4_file_getattr,
.listxattr = ext4_listxattr,
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
.fiemap = ext4_fiemap,
.fileattr_get = ext4_fileattr_get,
.fileattr_set = ext4_fileattr_set,
};
索引节点的增强属性
- inode索引节点的大小一般128B,当需要增加属性的时候,就会 使用inode的i_file_acl_lo字段指向增强属性。
#索引节点增强属性描述符(属性名,属性值) 为了实现访问控制列表
struct ext4_xattr_entry {
__u8 e_name_len; /* length of name */
__u8 e_name_index; /* attribute name index */
__le16 e_value_offs; /* offset in disk block of value */
__le32 e_value_inum; /* inode in which the value is stored */
__le32 e_value_size; /* size of attribute value */
__le32 e_hash; /* hash value of name and value */
char e_name[]; /* attribute name */
};
ext4_xattr_set() ext4_xattr_get() ext4_xattr_list_entries()等函数处理该属性。
目录
- 目录是一种特殊的文件,这种文件的数据块内存放的数据是目录名称和索引节点
##目录项结构
struct ext4_dir_entry_2 {
__le32 inode; /* Inode number */
__le16 rec_len; /* Directory entry length */
__u8 name_len; /* Name length 最大255B*/
__u8 file_type; /* See file type macros EXT4_FT_* below */
char name[EXT4_NAME_LEN]; /* File name */
};
目录的查找
- 如果目录的inode的flag被设置为EXT4_INDEX_FL (0x1000),则目录项对象使用hash btree(htree)组织,加快查找速度。
- hash btree 参考:https://blog.csdn.net/yang_yulei/article/details/46337405

struct dx_entry
{
__le32 hash;
__le32 block;
};
/*
* dx_root_info is laid out so that if it should somehow get overlaid by a
* dirent the two low bits of the hash version will be zero. Therefore, the
* hash version mod 4 should never be 0. Sincerely, the paranoia department.
*/
struct dx_root -------------- hash btree的根节点
{
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[];
};
struct dx_node ---------------------子节点
{
struct fake_dirent fake;
struct dx_entry entries[];
};
struct dx_map_entry
{
u32 hash;
u16 offs;
u16 size;
};
/*
* This goes at the end of each htree block.
*/
struct dx_tail { ---------------------------校验和
u32 dt_reserved;
__le32 dt_checksum; /* crc32c(uuid+inum+dirblock) */
};
文件(数据块)在磁盘中如何寻址(文件块的组织方式) extent tree(B tree)
- ext4 inode 的i_block字段60B, 可以包含一个ext4_extent_header, 4个ext4_extent_idx, 还剩下4B校验和。

#4B 的校验和
struct ext4_extent_tail {
__le32 et_checksum; /* crc32c(uuid+inum+extent_block) */
};
/*
* This is the extent on-disk structure.
* It's used at the bottom of the tree.
*/
## extent tree的页节点 12B
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.
*/
## extent 的索引节点 12B
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;
};
/*
* Each block (leaves and indexes), even inode-stored has header.
*/
## 头节点 12B
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 */
};
文件的洞
- 当要删除文件的尾部数据的时候可以调用ext4_truncate()函数, 释放文件尾部的数据块
- 要删除文件中间部分的数据的时候就产生洞,调用ext4_punch_hole() 释放文件的中间的数据块。虚拟化,云计算,数据库等场景下的大文件需要。 虽然文件看起来很大,但是使用的空间很小。
日志的写入
-
journal :将metadata和data全部记录到日志;先写日志确保日志写入成功再写用户数据 fsync(data journal) -> fsync(metadata journal) -> fsync(data) -> fsync(metadata) 优缺点:当写入数据很大时,性能低,安全性最高
-
ordered :只将metadata写入到日志;先写用户数据(data),之后再写日志,最后写用户数据(metadata) fsync(data) -> fsync(metadata journal) -> fsync(metadata) —ext4的默认方式。 优缺点:当写入数据很大时,性能低,安全性最高
-
writeback :只将metadata写入到日志,先写日志确保日志写入成功之后再写用户数据,但不保证metadata和data的写入顺序 fsync(metadata journal) -> fsync(metadata) / fsync(data) 优缺点:性能最好,安全性最低
参考:
- https://blog.csdn.net/cumj63710/article/details/107393126
- https://blog.csdn.net/qq_40174198/article/details/109294578?spm=1001.2101.3001.6650.10&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-10.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-10.no_search_link
- https://blog.csdn.net/RJ0024/article/details/110492406?spm=1001.2101.3001.6650.15&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-15.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-15.no_search_link
- https://ext4.wiki.kernel.org/index.php/Ext4_Disk_Layout#Multiple_Mount_Protection