Ext文件系统
1.文件目录
1.1 文件控制块FCB
- 文件系统通过文件控制块(File Control Blcok)来维护文件结构,FCB包含有关文件的信息,包括所有者、权限、文件内容的位置等
- 文件目录用于组织文件,每个目录项对应一个FCB
- 文件目录实现的关键
- FCB与文件内容的关联方法
- 在目录中“按名”搜索的效率
1.2 INode
- UFS(Unix系统使用的文件系统)中的FCB被称作索引结点inode,每个inode都有一个唯一的编号,包含的内容有
- inode编号
- 用来识别文件类型,以及用于 stat C 函数的模式信息
- 文件的链接数目
- 属主的ID (UID)
- 属主的组 ID (GID)
- 文件的大小
- 文件所使用的磁盘块的实际数目
- 最近一次修改的时间
- 最近一次访问的时间
- 最近一次更改的时间
2.Ext文件系统
虽然是为Linux编写的,但EXT文件系统起源于Minix操作系统,而Minix文件系统早在1987年首次发布,比Linux还早五年就已经发布了。如果我们查看EXT文件系统家族从其Minix根开始的历史和技术演变,就会更容易理解EXT4文件系统。
2.1 Minix
搬运自:https://www.oschina.net/translate/introduction-ext4-filesystem?lang=chs&p=1
https://zhuanlan.zhihu.com/p/502572979
当编写原始Linux内核,Linus Torvalds需要一个文件系统,但是不想开发它。因此他简单的使用了Minix文件系统,这是 Andrew S. Tanenbaum开发的,而且是Tanenbaum 的Minix操作系统的一部分。Minix是类Unix操作系统,为教育使用而开发。它的代码开放使用,而且合理的授权给Torvalds,允许他将它用于Linux的初代版本。
Minix结构如下,其中大部分位于文件系统生成的分区中:
- 引导扇区安装于硬盘的第一个扇区。引导块(boot block)包含一个非常小的引导记录和一个分区表。
- 每一个分区中的第一个块是超级块(superblock),它包含了定义其他文件系统结构的元数据,并将它们定位在分配给分区的物理磁盘上。
- 节点位图块(inode bitmap block),它确定了哪个节点在使用以及哪个节点是空闲的。
- 节点(inodes),它们在磁盘上有它们自己的空间。每个节点包含了一个文件的信息,包括数据块的位置,即文件所属的区域。
- **区域位图(zone bitmap)**跟踪记录数据区域的使用和释放。
- 数据区域(data zone),数据实际上存储的位置
这是大佬Linux内核库
dump出来的minix格式化后的磁盘数据
2.1.1 超级块
- 超级块代表了整个文件系统,超级块是文件系统的控制块,有整个文件系统信息,在格式化操作完成后,超级块的信息基本就已经定型了,可以说,一个超级块就代表了一个文件系统。
- 当我们在linux终端mount某个块设备时,内核会读取超级块的信息,完成文件系统的挂载。
超级块的数据结构为:
/*
* minix super-block data on disk
*/
struct minix_super_block {
__u16 s_ninodes;
__u16 s_nzones;
__u16 s_imap_blocks;
__u16 s_zmap_blocks;
__u16 s_firstdatazone;
__u16 s_log_zone_size;
__u32 s_max_size;
__u16 s_magic;
__u16 s_state;
__u32 s_zones;
};
可以看到结构体中部分成员变量的类型为u16(无符号short短整数类型),总共占用16Bit(=2Byte),而dump出来的数据都是小端模式,上图dump出来点的数据,每2位形成1组,大小为1Byte。所以在读取的时候u16对应的成员变量数据即为2组,然后倒序就是真正对应的数据含义。
2.1.2 inode节点
由于博主Linux内核库
在创建的时候只是简单的进行了格式化,所以只有一个根目录,没有其他任何子目录
-
在超级块的属性当中有一个s_firstdatazone属性,该值作为查找文件的门户,其指向的是根目录的数据区,176 x 1024 = 0x2c000。
-
在文件系统的inode区,第一个inode节点便是根目录节点
inode节点对应的数据结构为
/*
* This is the original minix inode layout on disk.
* Note the 8-bit gid and atime and ctime.
*/
struct minix_inode {
__u16 i_mode;
__u16 i_uid;
__u32 i_size;
__u32 i_time;
__u8 i_gid;
__u8 i_nlinks;
__u16 i_zone[9];
};
我们看一下inode bit map中指明的第一个inode,也就是根目录
在inode中会告诉这个文件对应的数据区域位置,这里正好也是176,对应了超级块里面的第一个inode位置176。
2.1.3 第一个数据块
在前面可以看到,在inode和超级块中都指明了第一个数据块为176,换算之后为0x2C000,。
2.2 ext2
部分内容搬运自:https://blog.csdn.net/m0_65931372/article/details/126368087?spm=1001.2014.3001.5506
https://blog.csdn.net/sinat_37817094/article/details/125716792
EXT2 第二代扩展文件系统 ( secondextended filesystem,缩写为 ext2),是LINUX内核所用的文件系统。它开始由Rémy Card设计,用以代替ext,于1993 年1月加入 linux 核心支持之中。ext2 的经典实现为 LINUX 内核中的 ext2fs 文件系统驱动,最大可支持2TB的文件系统,至linux核心2.6版时,扩展到可支持 32TB。
ext2文件系统是 Linux 系统中的标准文件系统,是通过对 Minix 的文件系统进行扩展而得到的,其存取文件的性能极好。在ext2 文件系统中,文件由 inode(包含有文件的所有信息)进行唯一标识。一个文件可能对应多个文件名,只有在所有文件名都被删除后,该文件才会被删除。此外,同一文件在磁盘中存放和被打开时所对应的 inode是不同的,并由内核负责同步。
从ext2文件系统的结构来看,可以发现基本与minix相同,但是神奇的是ext2文件系统竟然多出来了一个Block Group。个人理解,由于ext2文件系统可以支持更多的文件数量,为了方便管理数量庞大的文件,就像班级一样划分为一个一个的组。块被聚在一起分成几个大的块组(block group)。每个块组中有多少个块是固定的(最后一个块组除外,其所管理的块可能会少一些)。
EXT文件系统将整个分区划成若干个同样大小的块组,每个块组都由以下部分组成:超级块(superblock)、块组描述符表(GDT)、预留块组描述符表(reserved GDT)、块位图(block bitmap)、节点位图(inode bitmap)、节点表(inode table)和数据块(datablock)。
amx@amxxxx:/mnt$ sudo dumpe2fs /dev/sda1
dumpe2fs 1.44.5 (15-Dec-2018)
Filesystem volume name: <none>
Last mounted on: <not available>
Filesystem UUID: 89bdc55e-2f1e-4617-959e-17a757570a2f
Filesystem magic number: 0xEF53
Filesystem revision #: 1 (dynamic)
Filesystem features: ext_attr resize_inode dir_index filetype sparse_super large_file
Filesystem flags: signed_directory_hash
Default mount options: user_xattr acl
Filesystem state: not clean
Errors behavior: Continue
Filesystem OS type: Linux
Inode count: 131072
Block count: 524288
Reserved block count: 26214
Free blocks: 515284
Free inodes: 131061
First block: 0
Block size: 4096
Fragment size: 4096
Reserved GDT blocks: 127
Blocks per group: 32768
Fragments per group: 32768
Inodes per group: 8192
Inode blocks per group: 512
Filesystem created: Fri Jan 27 22:41:25 2023
Last mount time: n/a
Last write time: Fri Jan 27 22:45:21 2023
Mount count: 1
Maximum mount count: -1
Last checked: Fri Jan 27 22:41:25 2023
Check interval: 0 (<none>)
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
Default directory hash: half_md4
Directory Hash Seed: ea6cdeac-8c4b-408d-b075-1c2c9783f720
Group 0: (Blocks 0-32767)
Primary superblock at 0, Group descriptors at 1-1
Reserved GDT blocks at 2-128
Block bitmap at 129 (+129)
Inode bitmap at 130 (+130)
Inode table at 131-642 (+131)
32119 free blocks, 8181 free inodes, 2 directories
Free blocks: 649-32767
Free inodes: 12-8192
Group 1: (Blocks 32768-65535)
Backup superblock at 32768, Group descriptors at 32769-32769
Reserved GDT blocks at 32770-32896
Block bitmap at 32897 (+129)
Inode bitmap at 32898 (+130)
Inode table at 32899-33410 (+131)
32125 free blocks, 8192 free inodes, 0 directories
Free blocks: 33411-65535
Free inodes: 8193-16384
Group 2: (Blocks 65536-98303)
Block bitmap at 65536 (+0)
Inode bitmap at 65537 (+1)
Inode table at 65538-66049 (+2)
32254 free blocks, 8192 free inodes, 0 directories
Free blocks: 66050-98303
Free inodes: 16385-24576
.....
.....
2.2.1 准备工作
我们在自己的虚拟机上新建一个5G的硬盘,方便我们后面划分分区和进行格式化。
创建后通过fdisk -l
可以看到我们刚刚创建的那个5G的硬盘设备
1.划分分区【创建一个2G的sda1和3G的sda2】
amx@amxxxx:~/Desktop$ sudo fdisk /dev/sda
Welcome to fdisk (util-linux 2.33.1).
Changes will remain in memory only, until you decide to write them.
Be careful before using the write command.
Device does not contain a recognized partition table.
Created a new DOS disklabel with disk identifier 0xfd9f3091.
Command (m for help):
Command (m for help): g
Created a new GPT disklabel (GUID: 70283E07-CB9E-4D44-A4D0-FD62DCFE6691).
Command (m for help): n
Partition number (1-128, default 1):
First sector (2048-10485726, default 2048):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (2048-10485726, default 10485726): +2G
Created a new partition 1 of type 'Linux filesystem' and of size 2 GiB.
Command (m for help): n
Partition number (2-128, default 2):
First sector (4196352-10485726, default 4196352):
Last sector, +/-sectors or +/-size{K,M,G,T,P} (4196352-10485726, default 10485726):
Created a new partition 2 of type 'Linux filesystem' and of size 3 GiB.
Command (m for help): w
The partition table has been altered.
Calling ioctl() to re-read partition table.
Syncing disks.
2.格式化为ext2文件系统
amx@amxxxx:~/Desktop$ sudo mkfs.ext2 /dev/sda1
mke2fs 1.44.5 (15-Dec-2018)
Creating filesystem with 524288 4k blocks and 131072 inodes
Filesystem UUID: 89bdc55e-2f1e-4617-959e-17a757570a2f
Superblock backups stored on blocks:
32768, 98304, 163840, 229376, 294912
Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
可以看到我们创建了524288个块(4kb大小的块),一共131072个inode
3.挂载分区
amx@amxxxx:/mnt$ sudo mount /dev/sda1 /mnt/linux/
2.2.2 超级块
superblock:记录文件系统的整体信息,包含inode/block的大小、总量、使用量、剩余量,以及文件系统的格式,文件系统挂载时间,最近一次数据写入时间,最近一次校验磁盘的时间等。
超级块存储了EXT文件系统的全局配置参数(如数据块大小、总块数和索引节点总数等,这些参数在文件系统创建时就确定下来了)和动态信息(如当前空闲块数和空闲索引节点数等,可以在运行中改变)。对超级块的访问必须是互斥的,即任一时刻最多只能有一个进程访问超级块。
超级块记录了整个文件系统的各种信息,包括逻辑块的数量、inode数量、支持的特性和维护信息等内容。为了保证整个文件系统的完整性,例如突然断电或者系统崩溃等场景,文件系统出现元数据损坏的情况,Ext2文件系统对超级块进行了备份。这样可以保证即使在第一个超级块出现损坏的情况下,仍然可以通过其它块组中的超级块进行恢复,不至于整个文件系统都不可访问。
/*
* Structure of the super block
*/
struct ext2_super_block {
__le32 s_inodes_count; /* Inodes count */
__le32 s_blocks_count; /* Blocks count */
__le32 s_r_blocks_count; /* Reserved blocks count */
__le32 s_free_blocks_count; /* Free blocks count */
__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_frag_size; /* Fragment size */
__le32 s_blocks_per_group; /* # Blocks per group */
__le32 s_frags_per_group; /* # Fragments per group */
__le32 s_inodes_per_group; /* # Inodes per group */
__le32 s_mtime; /* Mount time */
__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 */
__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 */
__le16 s_def_resuid; /* Default uid for reserved blocks */
__le16 s_def_resgid; /* Default gid for reserved blocks */
/*
* These fields are for EXT2_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 */
__le32 s_feature_incompat; /* incompatible feature set */
__le32 s_feature_ro_compat; /* readonly-compatible feature set */
__u8 s_uuid[16]; /* 128-bit uuid for volume */
char s_volume_name[16]; /* volume name */
char s_last_mounted[64]; /* directory where last mounted */
__le32 s_algorithm_usage_bitmap; /* For compression */
/*
* Performance hints. Directory preallocation should only
* happen if the EXT2_COMPAT_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 */
__u16 s_padding1;
/*
* Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set.
*/
__u8 s_journal_uuid[16]; /* uuid of journal superblock */
__u32 s_journal_inum; /* inode number of journal file */
__u32 s_journal_dev; /* device number of journal file */
__u32 s_last_orphan; /* start of list of inodes to delete */
__u32 s_hash_seed[4]; /* HTREE hash seed */
__u8 s_def_hash_version; /* Default hash version to use */
__u8 s_reserved_char_pad;
__u16 s_reserved_word_pad;
__le32 s_default_mount_opts;
__le32 s_first_meta_bg; /* First metablock block group */
__u32 s_reserved[190]; /* Padding to the end of the block */
};
在超级块中,魔术标签是一个很重要的字段,在EXT2/EXT3/EXT4中,这个字段的值为0xEF53,否则这个分区就不是一个正常的EXT2/EXT3/EXT4文件系统。
amx@amxxxx:/mnt$ sudo hexdump -n 1024 -s 1024 /dev/sda1
0000400 0000 0002 0000 0008 6666 0000 dcd4 0007
0000410 fff5 0001 0000 0000 0002 0000 0002 0000
0000420 8000 0000 8000 0000 2000 0000 0000 0000
0000430 e381 63d3 0001 ffff ef53 0000 0001 0000 // 注意这里
0000440 e295 63d3 0000 0000 0000 0000 0001 0000
0000450 0000 0000 000b 0000 0100 0000 0038 0000
0000460 0002 0000 0003 0000 bd89 5ec5 1e2f 1746
0000470 9e95 a717 5757 2f0a 0000 0000 0000 0000
0000480 0000 0000 0000 0000 0000 0000 0000 0000
*
00004c0 0000 0000 0000 0000 0000 0000 0000 007f
00004d0 0000 0000 0000 0000 0000 0000 0000 0000
00004e0 0000 0000 0000 0000 0000 0000 6cea acde
00004f0 4b8c 8d40 75b0 2c1c 8397 20f7 0001 0000
0000500 000c 0000 0000 0000 e295 63d3 0000 0000
0000510 0000 0000 0000 0000 0000 0000 0000 0000
*
0000550 0000 0000 0000 0000 0000 0000 0020 0020
0000560 0001 0000 0000 0000 0000 0000 0000 0000
0000570 0000 0000 0000 0000 0000 0000 0000 0000
*
0000800
另一个重要的字段是每个块占用的字节数,其对应的数据结构是s_log_block_size,标识文件系统中块的大小(以字节为单位)为(B=1<<(s_log_block_size+10))。例如,如果值为0时,则块的大小为1024B,也是最小的块大小;如果值为2,那么块的大小为4096B。
历史记录
- 2023/01/27:第一次撰写《ext文件系统》,主要完成minix部分的整理