文件系统是操作系统管理存储的核心机制。本文将从磁盘硬件原理出发,剖析 Linux 中经典的 ext 文件系统如何组织数据、管理文件,并揭示 inode、块、软硬链接等关键概念的底层实现。
一、硬件理解
文件存储通常位于计算机硬盘上,属于一种'永久性'存储,硬盘有固态硬盘(SSD)和机械硬盘(HDD)。
1.1 磁盘物理结构
扇区是从磁盘读出和写入信息的最小单位,通常为 512 字节。
- 磁头(head)数:每个盘片一般对应 1 个磁头。
- 磁道(track)数:从盘片外圈往内圈编号的同心圆。
- 柱面(cylinder)数:磁道构成柱面,数量等同于磁道个数。
- 扇区(sector)数:每个磁道被切分成扇形区域。
- 磁盘容量 = 磁头数 × 磁道 (柱面) 数 × 每道扇区数 × 每扇区字节数。
CHS 寻址通过柱面、磁头、扇区定位。现代系统使用 LBA 地址。
CHS 转 LBA: LBA = 柱面号 C × (磁头数 × 每磁道扇区数) + 磁头号 H × 每磁道扇区数 + 扇区号 S - 1
LBA 转 CHS: 柱面号 C = LBA // (磁头数 × 每磁道扇区数) 磁头号 H = (LBA % (磁头数 × 每磁道扇区数)) // 每磁道扇区数 扇区号 S = (LBA % 每磁道扇区数) + 1
1.2 磁盘的逻辑结构
在 OS 看来,磁盘是一个元素为扇区的一维数组,下标为 LBA 地址。
二、Ext 文件系统
2.1 文件属性与分区
操作系统以'块'为单位进行操作,常见大小为 4KB(8x512 字节)。
- 块号 = LBA / 8
- 文件 = 属性 + 内容。元信息存储在 inode(索引节点)中。
ls -i可查看 inode 编号。- 文件名未纳入 inode 数据结构内部,inode 大小固定(通常 128 字节)。
Linux 早期文件系统为 ext2,后发展为 ext3/ext4,核心设计未变。
对磁盘的管理类似分治思想,linux 系统上柱面是最小的分区单位。
2.2 组管理字段
文件系统以分区为单位,不同分区可用不同文件系统管理。
Data Blocks:存放文件内容。inode Table:存放文件属性(inode 编号、类型、权限、大小等)。Block Bitmap:记录 Data Blocks 是否被占用。inode Bitmap:记录 inode Table 中各 inode 是否被使用。GDT:块组描述符,记录各块区域的起始地址、结束地址。Super Block:记录文件系统类型、版本、总块数、空闲块数、inode 起始地址等。
格式化操作本质是将 Super Block 初始化,将位图清 0。
2.3 inode 编号查询文件
inode 在分区内唯一。通过 Super Block 可知组中 inode 个数 w。
- 组号 = inode 编号 / w
- inode Bitmap 位置 = inode 编号 % w
目录本质也是文件,其内容是目录内部文件的文件名和 inode 编号的映射。
挂载示例:
dd if=/dev/zero of=./disk.img bs=1M count=5 # 制作一个大的磁盘块
mkfs.ext4 disk.img # 格式化写入文件系统
mkdir /mnt/mydisk # 建立空目录
sudo mount -t ext4 ./disk.img /mnt/mydisk/ # 将分区挂载到指定的目录
sudo umount /mnt/mydisk # 卸载分区
访问目录即访问分区,dentry 里面可以判断分区。
2.4 路径缓存(目录树)
路径解析效率低,操作系统会将历史访问的目录逐步形成目录树(多叉树),即路径缓存。
验证方法:
find / -name test.c
第一次查找慢(IO 操作),再次查找快(内存缓存)。
struct dentry 结构用于路径缓存,既属于多叉树,又是淘汰链表,还是哈希。
struct dentry {
atomic_t d_count;
unsigned int d_flags;
spinlock_t d_lock;
struct inode *d_inode;
struct hlist_node d_hash;
struct dentry *d_parent;
struct qstr d_name;
struct list_head d_lru;
// ... 其他字段
};
d_hash:哈希表用来快速查询d_lru:淘汰链表,用于内存回收d_parent:指向父目录d_inode:inode 结构的地址
struct inode {
unsigned long i_ino;
atomic_t i_count;
umode_t i_mode;
unsigned int i_nlink;
uid_t i_uid;
gid_t i_gid;
loff_t i_size;
// ... 其他字段
};
2.5 inode 与 Data Blocks 的映射
文件属性的 inode 结构里存储了一个数组,存储了文件内容相关的块地址。
EXT2_N_BLOCKS 定义为 15。前 12 直接索引,后面的是一级、二级、三级索引。
如果文件非常大,Data Block 存不下,文件内容可以跨组保存。
2.6 文件结构图解
fs是'导航仪':告诉我'我在哪'(根目录、当前目录);files是'文件夹':告诉我'我打开了哪些文件'(fd 到 file 的映射)。
三、软硬连接原理
3.1 软连接
创建软连接:ln -s 原文件 目标文件
软连接是一个独立的文件,有独立的 inode number,属性变为 l,内容存储的是原文件路径。
Windows 系统下的快捷方式相当于软连接。
3.2 硬链接
创建硬链接:ln 原文件 目标文件
硬链接的 inode 编号和原文件一样,本质是在目录下新建一个文件名与 inode 编号的映射关系。
引用计数从 1 变成 2。删除文件时计数器减 1,除非计数器减到 0 才会被删。
注意:硬链接只能给普通文件进行建立,Linux 系统不支持给目录建立硬链接。但软连接没此限制。
. 和 .. 目录本质是硬链接,但系统做了特殊处理,防止路径查找瘫痪。


