文件系统:FCB与索引节点
你有没有想过,当你保存一个文件时,这个文件到底存在了哪里? 操作系统是怎么知道这个文件有多长、叫什么、在磁盘的哪个位置?
答案就在两个核心概念里:FCB和索引节点(inode)。
从文件到磁盘:中间发生了什么?
┌──────────────────────────────────────────────────────────┐
│ 文件系统层次结构 │
├──────────────────────────────────────────────────────────┤
│ │
│ 用户程序 │
│ ↓ │
│ 系统调用(open, read, write, close) │
│ ↓ │
│ 虚拟文件系统(VFS) │
│ ↓ │
│ 具体文件系统(Ext4, NTFS, FAT32) │
│ ↓ │
│ 块设备层(磁盘控制器) │
│ ↓ │
│ 物理磁盘(扇区) │
│ │
└──────────────────────────────────────────────────────────┘FCB:文件控制块
FCB(File Control Block)是文件系统中描述一个文件的数据结构。 每个文件都有一个FCB,包含文件的所有元数据。
┌──────────────────────────────────────────────────────────┐
│ FCB结构(典型) │
├──────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 文件名 │ 文件类型 │ 访问权限 │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ 文件大小 │ 创建时间 │ 修改时间 │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ 所有者ID │ 所属组ID │ 链接数 │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ 文件数据位置 │ (磁盘块号列表) │ │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ 文件状态 │ 引用计数 │ │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘目录项(Directory Entry)
FCB太大了,不能直接放在目录里。所以目录里放的是目录项,是FCB的简化版。
┌──────────────────────────────────────────────────────────┐
│ 目录项结构 │
├──────────────────────────────────────────────────────────┤
│ │
│ Linux (Ext2/3/4): │
│ ┌──────────────────────────────┐ │
│ │ inode编号 (4字节) │ 文件名 (可变长) │ │
│ └──────────────────────────────┘ │
│ │
│ FAT32: │
│ ┌──────────────────────────────┐ │
│ │ 文件名 (8+3字节) │ FCB起始簇号 │ │
│ └──────────────────────────────┘ │
│ │
│ NTFS: │
│ ┌──────────────────────────────────────────────┐ │
│ │ 文件名 (Unicode) │ MFT记录号 │ │
│ └──────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘inode:Unix的革命性设计
inode(Index Node)是Unix/Linux文件系统的心脏。 它解决了FCB的直接访问问题——目录项只需要存储inode编号。
┌──────────────────────────────────────────────────────────┐
│ inode结构(Ext4) │
├──────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ mode (文件类型 + 权限) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ uid (所有者用户ID) │ size (文件大小) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ atime (访问时间) │ mtime (修改时间) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ ctime (属性改变时间) │ dtime (删除时间) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ gid (所属组ID) │ links_count (链接数) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ blocks (占用块数) │ flags (Ext4特性) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ 15个块指针: │ │
│ │ 12个直接块 │ │
│ │ 1个一级间接块 │ │
│ │ 1个二级间接块 │ │
│ │ 1个三级间接块 │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────┘inode如何定位文件数据?
inode中的15个块指针:
┌──────────────────────────────────────────────────────────┐
│ │
│ 直接块指针(12个): │
│ ┌──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐ │
│ │D0│D1│D2│D3│D4│D5│D6│D7│D8│D9│D10│D11│ │
│ └──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘ │
│ 每个指向一个磁盘数据块 │
│ │
│ 一级间接块(1个): │
│ ┌─────────────────────┐ │
│ │ 间接块指针表 │ ──→ [P0][P1][P2]...[P255] │
│ └─────────────────────┘ ↓ │
│ 数据块D │
│ │
│ 二级间接块(1个): │
│ ┌─────────────────────┐ │
│ │ 二级间接指针表 │ ──→ [I1][I2]...[I255] │
│ └─────────────────────┘ ↓ │
│ ┌─────────────────────┐ │
│ │ 一级间接指针表 │ │
│ └─────────────────────┘ │
│ ↓ │
│ [P0][P1]...[P255] │
│ ↓ │
│ 数据块D │
│ │
│ 三级间接块(1个): │
│ ┌─────────────────────┐ │
│ │ 三级间接指针表 │ │
│ └─────────────────────┘ │
│ ↓ │
│ 指向多个二级间接块 │
│ ↓ │
│ 指向多个一级间接块 │
│ ↓ │
│ 指向多个数据块 │
│ │
└──────────────────────────────────────────────────────────┘inode的最大文件大小
计算方式(假设块大小4KB):
直接块: 12 × 4KB = 48KB
一级间接: 256 × 4KB = 1MB
(256个指针,每个4字节)
二级间接: 256² × 4KB = 256MB
(256 × 256个指针)
三级间接: 256³ × 4KB = 64GB
总最大文件大小 ≈ 64GB + 256MB + 1MB + 48KB ≈ 64.26GB硬链接 vs 软链接
java
// 硬链接:多个目录项指向同一个inode
// 特点:
// - 不能跨文件系统
// - 不能链接目录
// - 删除只是减少链接数
// - 所有链接地位相同
public class HardLink {
public static void main(String[] args) throws IOException {
// 创建硬链接
// ln source.txt hardlink.txt
// 两个文件名指向同一个inode
// inode中的links_count = 2
}
}java
// 软链接(符号链接):一种特殊文件,内容是另一个文件的路径
// 特点:
// - 可以跨文件系统
// - 可以链接目录
// - 删除原文件,软链接失效
// - 类似Windows快捷方式
public class SoftLink {
public static void main(String[] args) throws IOException {
// 创建软链接
// ln -s source.txt softlink.txt
// softlink.txt的内容是 "source.txt"
}
}实际案例:Ext4文件系统
c
// Ext4的inode结构
struct ext4_inode {
__le16 i_mode; // 文件模式(类型+权限)
__le16 i_uid; // UID
__le32 i_size_lo; // 低32位文件大小
__le32 i_atime; // 访问时间
__le32 i_ctime; // 创建时间
__le32 i_mtime; // 修改时间
__le32 i_dtime; // 删除时间
__le32 i_gid; // GID
__le16 i_links_count; // 硬链接计数
__le32 i_blocks_lo; // 占用的块数(512字节单位)
__le32 i_flags; // 文件标志
__le32 i_osd1; // OS相关
__le32 i_block[15]; // 15个块指针!
__le32 i_generation; // 文件版本(用于NFS)
__le32 i_file_acl_lo; // ACL
__le32 i_size_high; // 高32位文件大小
__le32 i_obso_faddr; // 废弃
// ...
};Ext4的改进:
- Extent存储:用区间代替块指针,减少元数据开销
- 延迟分配:优化写入性能
- 日志(Journal):保证文件系统一致性
面试追问方向
- FCB和inode的区别是什么? 提示:FCB在内存,inode在磁盘。
- inode如何定位大文件的数据块? 提示:直接块、一级/二级/三级间接块。
- 硬链接和软链接的区别是什么? 提示:是否共享inode、能否跨文件系统、能否链接目录。
- 为什么目录也是一个文件? 提示:目录内容是目录项的列表。
