跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

Linux VFS:把设备和文件统一起来的那一层

Linux 的“一切皆文件”依赖 VFS 来落地:它在用户态统一暴露 open、read、write、close 等接口,在内核态通过 super_block、inode、dentry、file 这几类对象管理文件系统、路径和进程打开的文件;再借助 file_operations 里的函数指针,把具体读写请求分发到 ext4、设备驱动等不同实现。这样既抹平了设备和文件系统的差异,也保留了足够的扩展性。

BigDataPan发布于 2026/6/30更新于 2026/7/2310 浏览
Linux VFS:把设备和文件统一起来的那一层

Linux VFS:把设备和文件统一起来的那一层

先把前面的线接上

前一篇我们已经把 fd 讲清楚了:它本质上就是一个下标,dup2 则把这个下标指向的对象换掉。重定向能跑起来,靠的就是这层关系。

dup2 容易绕,我再用一句话压一下:前一个参数是目标 fd,后一个参数是要被复制来源的 fd。你可以把它理解成'把后面的内容搬到前面去'。

接下来就轮到 Linux 里那个更抽象、但也更关键的东西了——一切皆文件。

image.png

学完这一篇,重点就三件事:

  1. 怎么理解'一切皆文件'
  2. Linux 里几个核心结构体分别干什么
  3. VFS 是怎么借助多态把调用分发出去的

一切皆文件,不是口号

Linux 里很多资源都被包装成了'文件'。键盘、鼠标、屏幕、磁盘、网卡,甚至进程信息、Socket、管道,最后都能落到文件接口上。

image.png

这不是为了好看,而是为了统一。

开发者不用针对每种设备单独学一套 API。读文件、读网卡数据,走的都可能是 read;往文件写内容、往终端输出,也都可能是 write。接口一旦统一,系统层和上层代码都省事不少。

如果没有这套设计,情况会很碎:

  • 读磁盘要一套指令
  • 读麦克风要另一套指令
  • 读网络包又是另一套指令

Linux 直接把这些东西都伪装成文件,代价是抽象层多了一点,换来的好处是接口整齐得多。这个取舍我觉得很划算,尤其是系统编程里,统一接口比'看起来纯粹'更重要。

VFS 是什么

真正把这件事撑起来的,是 VFS(Virtual File System,虚拟文件系统)。它是 Linux 内核里的抽象层,上面接用户态的 open、read、write,下面兼容 ext4、NTFS、NFS、procfs 这类不同文件系统。

可以把它理解成一个中间层:上面的人只管按统一方式操作,下面的实现各干各的,VFS 负责把调用转过去。

这其实就是依赖倒置的典型味道:上层依赖抽象,不直接绑死具体实现。

VFS 的几个核心对象

理解 VFS,先认四个结构体。别急着背名字,先弄清它们各自负责哪一段。

struct super_block

它代表的是一个已经挂载的文件系统,比如一块磁盘分区、一个 U 盘,或者某个网络文件系统实例。

这里面放的是文件系统级别的元信息,比如块大小、魔数、文件系统类型、根目录位置。挂载时创建,卸载时销毁。

struct inode

它表示具体的文件或目录,是文件本体的元数据描述。

里面有大小、权限、时间戳、拥有者,以及数据块位置这些信息。它不包含文件名,这点很容易记混。名字不归 inode 管,inode 管的是'这个对象是什么'。

struct dentry

它是路径解析时的目录项,负责把'名字'和 inode 连起来。

比如 ,路径里的每一段都可以对应一个 dentry。内核还会缓存这些对象,也就是 dentry cache。这个缓存很实用,路径反复访问时能少走很多磁盘开销。真正线上排障或者高频访问场景里,这种缓存带来的收益比概念本身更值得在意。

/home/user/a.txt

struct file

它代表的是'进程打开的文件'。

这里面有当前偏移位置、打开方式、以及指向相关 dentry 的指针。open() 时创建,close() 时销毁。不同进程打开同一个文件,通常会有不同的 file 对象,因为读写位置不一定一样,但它们可能指向同一个 inode。

进程和文件是怎么连起来的

进程启动后,内核里会有对应的 task_struct。它里面会关联到 files_struct,再往下是一张文件表,文件表里保存着 fd 和 struct file * 的映射。

链路可以简单记成这样:

task_struct → files_struct → fd 表 → struct file

到了 struct file 这一步,真正有意思的地方才开始。

因为 struct file 里有个 f_op 指针,它指向 struct file_operations。而 struct file_operations 里面放的,全是函数指针。也就是说,open 的时候绑定了什么实现,后面读写就会走什么实现。

VFS 为什么像多态

Linux 底层是 C,但这不妨碍它做出多态的效果。它不是靠语言特性,而是靠结构体和函数指针硬做出来的。

先看一个最基础的接口定义:

// <linux/fs.h>
struct file_operations {
    struct module *owner;
    // 读文件的函数指针
    ssize_t (*read)(struct file *, char __user *, size_t, loff_t *);
    // 写文件的函数指针
    ssize_t (*write)(struct file *, const char __user *, size_t, loff_t *);
    // 打开文件的函数指针
    int (*open)(struct inode *, struct file *);
    // ... 其他操作如 poll, mmap, flush 等
}; 

这个结构体本身不做具体工作,它只是定义接口。真正的实现由不同驱动或文件系统填进去。

比如 ext4 可能会这样挂上自己的实现:

// ext4 的'多态'实现
const struct file_operations ext4_file_operations = {
    .read_iter = ext4_file_read_iter, // 指向 Ext4 具体的读函数
    .write_iter = ext4_file_write_iter,
    .open = ext4_file_open, // ...
}; 

鼠标这种字符设备也会有自己的一套:

// 鼠标驱动的'多态'实现
const struct file_operations mousedev_fops = {
    .read = mousedev_read, // 指向鼠标驱动具体的读函数
    .write = mousedev_write,
    .open = mousedev_open, // ...
}; 

名字都叫 read,但落到不同的结构体里,指向的实现完全不同。VFS 在调用时不关心底层是 ext4 还是鼠标设备,它只看 file->f_op 里到底绑了谁。

调用链大致是这样:

// 系统调用入口
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count) {
    // 1. 根据 fd 找到 file 结构体
    struct file *file = fget(fd); // 从 current->files->fd_array[fd] 获取
    // 2. 调用 VFS 层的通用 read 函数
    ret = vfs_read(file, buf, count, &pos);
    fput(file);
    return ret;
}

// VFS 层的 vfs_read(统一接口)
ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) {
    // 权限检查等...
    // 3. 关键的一行:多态调用
    if(file->f_op->read){
        return file->f_op->read(file, buf, count, pos);
    } else if(file->f_op->read_iter){
        // 或者使用 read_iter(新接口)
        // ...
    }
    return -EINVAL;
}

这就是 Linux 版的多态。vfs_read 自己不读数据,它只负责把请求交给当前文件对应的实现。

如果 file 指向普通文件,最后可能进到 ext4 的读函数;如果它指向设备文件,可能就落到设备驱动的实现里。对上层来说,接口没变;对内核来说,分发已经完成了。

对外是统一的 read()。 对内是 file->f_op->read 指向不同实现。 运行时根据绑定关系跳到真正的处理函数。

VFS 的作用

VFS 像一层润滑剂,把文件系统、设备文件、网络资源这些差异很大的东西抹平了。

image.png

它不负责把事情做得花哨,做的是把调用路径统一起来。这个设计看着朴素,但一旦系统规模变大,就会发现它特别能扛事。

小结

'一切皆文件'在 Linux 里不是修辞,是一套真正落地的设计。VFS 把磁盘、设备、网络、进程信息这些本来差异很大的资源,统一进同一套接口里。

它做的事情其实就两层:上面给出统一的 open、read、write、close,下面用函数指针把调用派发给具体实现。这样一来,用户态不用关心底层细节,内核也能同时兼容很多文件系统和设备模型。

这套架构不算轻巧,但很稳。对系统编程来说,稳比漂亮更值钱。

目录

  1. Linux VFS:把设备和文件统一起来的那一层
  2. 先把前面的线接上
  3. 一切皆文件,不是口号
  4. VFS 是什么
  5. VFS 的几个核心对象
  6. struct super_block
  7. struct inode
  8. struct dentry
  9. struct file
  10. 进程和文件是怎么连起来的
  11. VFS 为什么像多态
  12. VFS 的作用
  13. 小结
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • 前端常用加密方式与算法解析
  • Flutter 底部导航与 TabBar 多页切换实战及状态保持
  • OpenClaw 多飞书机器人绑定配置实战指南
  • 实战 LLaMA Factory:在国产 DCU 上高效微调 Llama 3 模型
  • STL 文件预览工具:使用 stl-thumb 生成 3D 模型缩略图
  • 无需公网 IP 安全远程访问本地 AI 服务方案
  • 纯 HTML 实现的交互式星球漫游效果
  • MySQL 8.4.7 在 Windows 10/11 下的免安装版部署指南
  • 大模型原理基础:从感知机到神经网络
  • Xilinx SRIO IP 核与 FPGA 实现仿真流程
  • 人工智能发展历程与现状分析
  • 基于 Microi 吾码的服务器虚拟化资源管理方案
  • Cesium 无人机智能航线规划:航点动作组与 AI 识别实战
  • Ubuntu 环境下 RabbitMQ 快速安装与配置指南
  • Harness Engineering 工程化教程:AI Agent 复杂长任务实践指南
  • OFA 图文蕴含模型部署:AI 绘画提示词与图像匹配度评分
  • Java Web 开发环境搭建:IDEA 与 Tomcat 安装部署指南
  • .NET 集成 GoView 低代码可视化大屏实战指南
  • 基于 DeepSeek 与腾讯云 HAI 设计个人网页
  • 2025 年 AIGC 技术发展的六大核心趋势

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online