EXT2文件系统
1.什么是EXT2文件系统
关于磁盘分区:
磁盘分区是告诉操作系统某块磁盘在此分区可以访问的区域,这样操作系统就知道它可以在所指定的块内进行文件数据的读、写、查找等操作。而指定分区的区域范围记录在第一个扇区的分区表中。分区表只有64字节(每个分区项占用16个字节,这16个字节中存有活动状态标志、文件系统标识、起止柱面号、磁头号、扇区号、隐含扇区数目(4个字节)、分区总扇区数目(4个字节)等内容。),所以最多只能记录4条分区的记录,这四条记录称为主分区或扩展分区。扩展分区还可以再分出逻辑分区。扩展分区最多只能有一个(操作系统限制),主分区和逻辑分区的内容可以被格式化,而扩展分区不能格式化。
一个磁盘可以划分成多个分区,每个分区必须先用格式化工具(如mkfs命令)格式化成某种格式的文件系统,然后才能存储文件,格式化过程中会在磁盘上写一些管理存储布局的信息。一个分区只能格式化成一个文件系统。我们如果想要获得EXT2文件系统,一般首先要做的就是创建一个分区,然后我们就可以存储这种类型的文件了~
如何创建虚拟磁盘:
在linux下,命令 mkef2s [-b blksize -N ninodes] device nblocks这条命令的意思是在设备上创建一个带有nblocks个块(每个块的大小为blksize字节,未指定blksize时默认块的大小是1KB)和ninodes个索引节点的EXT2文件系统。设备可以是真实设备,也可以是虚拟磁盘文件。
dd if=/dev/zero of=vdisk bs=1024 count=1440
mke2fs vdisk 1440
这几行语句的含义是在一个名为vdisk的虚拟磁盘文件上创建一个EXT2文件系统,有1440个大小为1KB的块。
2.引入超级区块
关于虚拟磁盘布局:
介绍完了EXT2文件系统,我又学习了文件系统是如何运行的——这与操作系统的文件数据有关。较新的操作系统的文件数据除了文件实际内容外, 通常含有非常多的属性,例如 Linux 操作系统的文件权限(rwx)与文件属性(拥有者、群组、时间参数等)。 文件系统通常会将这两部份的数据分别存放在不同的区块,权限与属性放置到 inode 中,至于实际数据则放置到 data block 区块中。 另外,还有一个超级区块 (superblock) 会记录整个文件系统的整体信息,包括 inode 与 block 的总量、使用量、剩余量等。
每个 inode 与 block 都有编号,至于这三个数据的意义可以简略说明如下:
| superblock | inode | block |
|---|---|---|
| 记录此 filesystem 的整体信息, 以及文件系统的格式与相关信息等 | 记录文件的属性,一个文件占用一个inode,同时记录此文件的数据所在的 block 号码 | 实际记录文件的内容,若文件太大时,会占用多个 block |
EXT2文件系统磁盘布局:

引导块(boot/B0):
其中boot(B0)为引导块,文件系统不会使用,用来容纳一个引导程序,从磁盘引导操作系统,在引导块之后才是EXT2文件系统的开始,文件系统将整个分区划分成若干个同样大小的块组(Block Group)。下面我们讲介绍一下每个块组的具体功能。
超级块(SuperBlock或B1):
超级块用于容纳整个文件系统的信息。超级块记录的信息有:
1、block 与 inode 的总量(分区内所有Block Group的block和inode总量);
2、未使用与已使用的 inode / block 数量;
3、block 与 inode 的大小 (block 为 1, 2, 4K,inode 为 128 bytes);
4、filesystem 的挂载时间、最近一次写入数据的时间、最近一次检验磁盘 (fsck) 的时间等文件系统的相关信息;
5、一个 valid bit 数值,若此文件系统已被挂载,则 valid bit 为 0 ,若未被挂载,则 valid bit 为 1 。
每个区段与 superblock 的信息都可以使用 dumpe2fs 这个命令查询
块组描述符(GDT/GD/B2):
EXT2将磁盘分成几个组,每个组有32K(在硬盘中),每组用一个块组描述符结构体来描述
每个块组描述符存储一个块组的描述信息,如在这个块组中从哪里开始是inode Table,从哪里开始是Data Blocks,空闲的inode和数据块还有多少个等等。对于Linux格式的EXT2文件系统保留了块3到块7。
块位图(Bamp/B8):
位图是用来表示某种项的位序列,例如磁盘块或索引节点,用于分配和回收项。
| 0位 | 1位 |
|---|---|
| 对应项处于FREE状态 | 对应项处于IN_USE状态 |
一个软盘有1440个块,但是B0未被使用,所以共有1439个有效位,其中无效位被视作IN_USE,设置为1
inode位图(inode Bitmap/Imap/B9)
inode位图和块位图类似,本身占一个块,其中每个bit表示一个inode是否空闲可用。一个索引节点就是用来代表一个文件的数据结构,各索引节点的状态用Imap中的一个位表示。Bitmap的作用是记录block group中Inode区域的使用情况。前10个索引点是预留的(默认1),然后在是0或1。
索引(开始)节点(inode table/B10)
每个文件都用一个128节点的唯一索引节点结构体来表示。而这些索引节点就是inode,inode table由一个块组中的所有inode组成。一个文件除了数据需要存储之外,一些描述信息也需要存储,如文件类型,权限,文件大小,创建、修改、访问时间等,这些信息存在inode中而不是数据块中。inode表占多少个块在格式化时就要写入块组描述符中。
inode主要记录的信息有:
·该文件的访问模式(rwx)
·该文件的所有者与组(owner/group)
·该文件的大小
·该文件创建或状态改变的时间(ctime)
·最近一次的读取时间(atime)
·最近修改内容的时间(mtime)
·定义文件特性的标志(flag),如SetUID等
·该文件真正内容的指向(Pointer)
i_mode为u16或者2字节无符号整数。
|4 | 3 | 9 |
i_mode=|tttt|ugs|rwxrwxrwx|
其中前四位定义了文件类型。tttt=10表示REG文件,0100表示DIR文件。ugs表示文件的特殊用法。最后9位用于文件保护的rwx权限位。
我们回到inode整体。每个 inode 大小均固定为 128 bytes,而每个文件都仅会占用一个 inode 而已;因此文件系统能够创建的文件数量与 inode 的数量有关。但是记录一个block号码要花去4byte,这样一看好像inode一共也无法记录多少个block了。其实不然,inode的存储block信息方式并不是我们想象的那么简单: inode 记录 block 号码的区域定义为12个直接,1个间接, 1个双间接与1个三间接记录区。
有一张图对于索引节点中的i_block数组有了较为清晰的解释:

一个inode占128字节,其中60个字节用于指向存放文件内容的数据块指针。每个指针4字节,那么有15个指针。最后3个指针用分级间接寻址。
12个直接指向(直接块)(i_block[0]~i_block[11],可以有12条记录。
一级间接寻址(间接块):指向一个包含256个块编号的磁盘块,每个块编号指向一个磁盘块可以有256条记录。
二级间接寻址(双重间接块),是一个指向256个块的块,而每个块又指向256个磁盘块,所以可以有256* 256条记录。
三级间接寻址(三重间接块),可以有256 * 256 * 256条记录。
所以对于1KB的块大小最大可以表示(256^3 +256^2+256+12)*1KB≈16GB的文件。
索引节点位置从0开始计数,而索引节点编号从1开始计数。在简单的EXT2文件系统中,索引节点的数量是184个,索引节点块数184/4=23个。索引节点块为B10至B32。
数据块(inode blocks)
索引节点块后面的是文件存储数据块。一般来说,第一个实际数据是B33,它就是根目录的i_block[0]。
根据不同的文件类型有以下几种情况:
·对于普通文件,文件的数据存储在数据块中。
·对于目录,该目录下的所有文件名和目录名存储在所在目录的数据块中,除了文件名外,ls -l命令看到的其它信息保存在该文件的inode中。
·对于符合链接,如果目标路径名较短则直接保存在inode中,如果较长则分配一个数据块来保存。
·设备文件、FIFO和socket等特殊文件没有数据块。
在格式化时block的大小就固定了, 因为 block 大小而产生的 Ext2 文件系统限制如下:
| Block大小 | 1KB | 2KB | 3KB |
|---|---|---|---|
| 最大单一文件限制 | 16GB | 256GB | 2TB |
| 最大文件系统总容量 | 2TB | 8TB | 16TB |
在一个名为vdisk的虚拟磁盘文件上创建一个EXT2文件系统

3.编程示例
显示超级块:
在输入课本程序前,系统需要安装ext2fs.h头文件,它定义了EXT2/3/4文件系统的数据结构。
但是当我在终端输入课本所给的代码sudo apt-get install ext2fs-dev时,却提示我找不到这个没有那个文件或目录。

我上网搜索资料,发现使用sudo apt-get install e2fslibs-dev e2fslibs-dev可以成功下载
我运行书本给的代码时,发现很多项都显示0,检查后发现是由于没有挂载的缘故,由于之前我已经将磁盘分好区了,所以可以直接挂载,由于之前我的sdb盘分出的sdb1分区是ext4文件系统,我重新将其更改为ext2文件系统,最后成功运行
挂载并将文件放到此文件系统目录下


ff.c程序显示EXT2文件系统的超级块信息运行结果:

课本代码如下:
/*********** superblock.c program ************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/io.h>
#include <ext2fs/ext2_fs.h>
// Cypedef u8, ul6, u32 SUPER for convenience typedef typedef typedef
typedef unsigned char u8;
typedef unsigned short ul6;
typedef unsigned int u32;
typedef struct ext2_super_block SUPER;
SUPER *sp;
char buf[1024];
int fd, blksize, inodesize;
int print(char *s, u32 x)
{
printf("%-30s = %8d\n", s, x);
}
int super(char *device)
{
fd = open(device, O_RDONLY);
if (fd < 0)
{
printf("open %s failed\n", device);
exit(1);
}
lseek(fd, (long)1024*1, 0); // block 1 or offset 1024
read(fd, buf, 1024);
sp = (SUPER *)buf; // as a super block structure
// check for EXT2 FS magic number
printf("%-30s = %8x ", "s_magic", sp->s_magic);
if (sp->s_magic != 0xEF53)
{
printf("NOT an EXT2 FS\n"); exit(2);
}
printf("EXT2 FS OK\n");
print("s_inodes_count", sp->s_inodes_count);
print("s_blocks _count", sp->s_blocks_count);
print("s_r_blocks_count", sp->s_r_blocks_count);
print("s_free_inodes_count", sp->s_free_inodes_count);
print("s_free_blocks_count", sp->s_free_blocks_count);
print("s_first_data_blcok", sp->s_first_data_block);
print("s_log_block_s i z e", sp->s_log_block_size);
print("s_blocks_per_group", sp->s_blocks_per_group);
print("s_inodes_per_group", sp-> s_inodes_per_group);
print("s_mnt_count", sp->s_mnt_count);
print("s_max_mnt_count", sp-> s_max_mnt_count);
printf("%-30s = %8x\n", "s_magic", sp->s_magic);
printf ("s_mtime = %s\n", ctime ((const time_t *)&sp->s_mtime));
printf ("s_wtime = %s", ctime ((const time_t *)&sp->s_wtime));
blksize = 1024 * (1 << sp->s_log_block_size);
printf("block size = %d\n", blksize);
printf("inode size = %d\n", sp->s_inode_size);
}
char *device = "vdisk"; // default device name
int main(int argc, char *argv[])
{
if (argc>1)
device = argv[1];
super(device);
}
4.遍历文件系统树
遍历算法:
1.读取超级块。验证其确实是EXT2 FS。
2.读取块组描述符块,以访问组0描述符,从inode_table中找到inode的起始编号。
3.读取inode起始编号,获取/的索引节点。
4.将路径名记为组件字符串。(假设组件数量为n,路径名为a/b/c,则组件字符串为"a","b","c",n=3,用name[0],name[1]...name[n-1]来表示组件。
5.从inode的根索引节点开始,在其数据块中搜索name[0],一个目录索引节点只有12个直接数据块。每个数据块都包含以下形式的dir_entey结构体:
[ino rec_len name_len NMAE] [ino rec_len name_len NAME]......
对于每块数据块,将该块读入内存,并用dir_entry *dp指向加载的数据块,然后使用name_len将NAME提取为字符串,并与name[0]进行比较。如果他们不匹配,就通过以下代码转到下一个dir_entry:
dp=(dir_entry*)((char*)dp+dp->rec_len);继续搜索。
6.使用索引节点号ino来定位相应的索引节点。使用邮差算法来计算包含索引节点的磁盘块及其在块中的偏移量。
blk=(ino-1)/INDOES_PER_BLOCK+InodesBeginBlock;
offset=(ino-1)%INODES_PER_BLOCK;
在索引节点中读取/a,判断其是否为一个目录,如不是,不能有/a/b,搜索失败;如是,继续搜索下一个组件name[1]。
EXT2文件系统数据结构:

(1)是当前运行的PROC结构体。每个文件操作都是由当前执行的进程决定的,每个进程都有一个cwd,指向进程当前的工作目录的内存索引节点。
(2)是文件系统的根指针,指向内存中的根索引节点。
(3)opentable条目,当某个进程打开某个文件时,它指向打开文件的内存索引节点。
(4)内存索引节点。当需要某个文件时,会把它的索引节点加载到minode槽内以供使用。
(5)是已挂载的文件系统表。
文件系统的级别:
文件系统的实现分为三个级别。每个级别处理文件系统的不同部分。
·第一级别实现了基本文件系统树,实现了指定函数
使用第一级别FS函数的用户命令程序有:mkdir、creat、rmdir、link、unlink、symlink、rm、ls、cd和pwd等。
·第二级别实现了文件内容读/写函数
比如:open_close_lseek.c、read.c、write.c、opendir_readdir.c
·第三级别实现了文件系统的挂载、卸载和文件保护
5.文件系统的结构
type.h文件:
sys/types.h,中文名称为基本系统数据类型,此头文件还包含适当时应使用的多个基本派生类型。这类文件包含ext2系统的数据结构类型,比如超级块、组描述符、索引节点和目录条目等结构。此外,它还包含打开文件表、挂载表、PROC结构体和文件系统常数。
以下为基本的派生类型

global.c文件:
这类文件包含文件系统的全局变量。当文件系统启动时,我们初始化所有全局数据结构,并让运行点位于PROC[0],即超级用户的进程P0(uid=0)。在实际操作中,每个操作都是由当前运行的进程决定的。从超级用户进程开始,因为它不需要任何文件保护。文件系统操作过程中,全局数据结构被视为系统资源,可灵活使用和释放。每一组资源都由一对分配和释放函数管理。例如,mialloc()分配一个空闲的minode供使用,而midalloc()则释放一个使用过的minode。
mount-root文件:
该文件包含mount_root()函数,在系统初始化期间调用该函数来挂载根文件系统。它读取根设备的超级块,以验证该设备是否为有效的EXT2文件系统。它分配一个挂载表条目来记录挂载的根文件系统。根设备的一些关键信息,如inode和块的数量、位图的起始块和inode表,也记录在挂载表中,以便快速访问。
如何执行ls:
ls列出了目录或文件的信息。其工作原理如下文所示:
①ls_dir(dirname):使用opendir()和readdir()获取目录中的文件名,对于每个文件名,调用ls_file(filename)。
②ls_file(filename):stat文件名,以在STAT结构体中获取文件信息。然后,列出STAT信息。
6.关于ext、ext2、ext3、ext4文件系统的区别与联系
在学习书本内容时,我发现Linux中有有两种较为常见的文件系统——ext2和ext4,我不是很理解这两种有什么区别,于是搜集了一些这方面的资料,整理如下:
EXT:第一代扩展文件系统, 于1992年4月发表,是为Linux核心所做的第一个文件系统。采用Unix文件系统(UFS)的元数据结构,以克服MINIX文件系统性能不佳的问题。 它是Linux上第一个利用虚拟文件系统实现的文件系统。
EXT2:第二代扩展文件系统 是Linux内核所用的文件系统。ext2 的经典实现为Linux内核中的ext2fs文件系统驱动,最大可支持2TB的文件系统,到Linux核心2.6版时,扩展至可支持32TB。在ext2文件系统中,文件由inode(包含有文件的所有信息)进行唯一标识。一个文件可能对应多个文件名,只有在所有文件名都被删除后,该文件才会被删除。此外,同一文件在磁盘中存放和被打开时所对应的inode是不同的,并由内核负责同步。
EXT3:ext3文件系统是直接从ext2文件系统发展而来,目前ext3文件系统已经非常稳定可靠。它完全兼容ext2文件系统。用户可以平滑地过渡到一个日志功能健全的文件系统中来。其具有以下优势:
1、高可用性:系统使用了ext3文件系统后,即使在非正常关机后,系统也不需要检查文件系统。
2、数据的完整性:避免了意外宕机对文件系统的破坏。
3、文件系统的速度:因为ext3的日志功能对磁盘的驱动器读写头进行了优化。所以,文件系统的读写性能较之ext2文件系统并来说,性能并没有降低。
4、数据转换 :由ext2文件系统转换成ext3文件系统非常容易。
5、多种日志模式。
EXT4:Ext4是Ext3的改进版,修改了Ext3中部分重要的数据结构,而不仅仅像Ext3对Ext2那样,只是增加了一个日志功能而已。Ext4可以提供更佳的性能和可靠性,还有更为丰富的功能。其具有以下优势:
1、与Ext3兼容:执行若干条命令,就能从Ext3在线迁移到Ext4,而无须重新格式化磁盘或重新安装系统。
2、更大的文件系统和更大的文件:较之Ext3目前所支持的最大16TB文件系统和最大2TB文件,Ext4分别支持1EB(1,048,576TB,1EB=1024PB,1PB=1024TB)的文件系统,以及16TB 的文件。
3、无限数量的子目录:Ext3目前只支持32,000个子目录,而Ext4支持无限数量的子目录。
4、Extents:Ext4引入了现代文件系统中流行的extents概念,每个 extent 为一组连续的数据块,相比Ext3采用间接块映射,提高了不少效率。
5、多块分配:Ext4 的多块分配器“multiblock allocator”(mballoc) 支持一次调用分配多个数据块。
6、延迟分配
7、快速 fsck
8、日志校验
9、“无日志”(No Journaling)模式
10、在线碎片整理
11、inode 相关特性:较之Ext3默认的inode大小128字节,ext4默认inode大小为256字节。
参考资料
https://blog.csdn.net/superSmart_Dong/article/details/120424307
https://blog.csdn.net/piupiu78/article/details/118691195
浙公网安备 33010602011771号