【底层机制】linux IO 为什么要有进程表项、文件表项、v节点表项、i节点表项

40 阅读3分钟

为什么设计成这样的分层架构?

1. 历史演进与兼容性需求

// 从Unix到Linux的演进
1970s: Unix V6/V7 → 1980s: BSD/SysV → 1990s: Linux
  • 向后兼容:需要支持几十年的应用程序和文件格式
  • 渐进式改进:在保持接口稳定的前提下优化内部实现

2. 抽象与解耦原则

应用层    → 统一的文件操作接口
文件系统层 → 多种文件系统实现
块设备层   → 多种存储设备

每层只关心自己的职责,降低系统复杂度。

3. 资源共享与进程隔离

  • 多个进程需要共享同一个文件
  • 每个进程需要有自己的文件位置和状态
  • 需要安全的权限控制

各层表项的详细作用分析

1. 进程表项 (Per-Process File Descriptor Table)

作用

// 每个进程独立的数据结构
struct files_struct {
    struct file **fd_array;  // 文件指针数组
    unsigned int next_fd;    // 下一个可用fd
};

核心价值

  • 进程隔离:每个进程有自己的文件视图
  • 简单的用户接口:用整数fd隐藏复杂的内核对象
  • 资源管理:进程退出时自动关闭所有文件

如果去掉进程表项

  • 进程无法独立管理打开的文件
  • 无法实现标准输入/输出/错误重定向
  • 进程间文件操作会相互干扰

2. 文件表项 (System-wide Open File Table)

作用

struct file {
    struct path f_path;           // 文件路径
    loff_t f_pos;                 // 当前偏移量
    atomic_long_t f_count;        // 引用计数
    const struct file_operations *f_op; // 操作函数
    unsigned int f_flags;         // 打开标志
};

核心价值

  • 打开实例管理:同一个文件可以被多次打开,每次有独立状态
  • 偏移量共享:fork()和dup()时共享文件位置
  • 状态维护:维护每个打开会话的特定状态

如果去掉文件表项

  • 无法支持O_APPEND等模式
  • 父子进程无法共享文件位置
  • 每次read/write都需要重新定位

3. v节点表项 (v-node Table)

作用

struct inode {  // Linux中vnode功能整合到inode
    umode_t i_mode;              // 文件类型和权限
    const struct inode_operations *i_op; // inode操作
    struct super_block *i_sb;    // 所属文件系统
    void *i_private;             // 文件系统私有数据
};

核心价值

  • 文件系统抽象:统一接口支持ext4、XFS、NFS等
  • 元数据缓存:缓存文件属性,减少磁盘访问
  • 操作分发:将通用操作路由到具体文件系统实现

如果去掉v节点

  • 每个文件系统都需要实现完整的VFS接口
  • 无法实现跨文件系统的通用工具
  • 文件系统开发复杂度大幅增加

4. i节点表项 (i-node Table)

作用

// 文件系统特定的inode(如ext4)
struct ext4_inode {
    __le16 i_mode;              // 文件模式
    __le32 i_size;              // 文件大小
    __le32 i_blocks;            // 块数量
    __le32 i_block[EXT4_N_BLOCKS]; // 数据块指针
};

核心价值

  • 磁盘数据结构:文件在磁盘上的物理表示
  • 元数据存储:文件大小、权限、时间戳等
  • 数据定位:通过块指针找到文件数据

如果去掉i节点

  • 文件系统无法在磁盘上组织数据
  • 无法实现文件的持久化存储
  • 基本的文件属性无法保存