Appearance
内核伪文件系统全景 —— debugfs、procfs、sysfs 们为什么不需要硬盘
上一篇 vfs-overview.md 讲 VFS 框架如何用
file_operations实现多态分发,fuse.md 讲怎么把文件系统代理到用户态。本篇聚焦内核自己提供的伪文件系统——那些不需要硬盘、不需要块设备、挂载后就能直接cat/echo的"虚拟目录"。回答几个核心问题:内核为什么需要这么多伪文件系统?每个是干什么的?从性能工程师视角看,哪些是关键数据源、哪些是控制入口?
更新时间:2026-08-06
一、什么是伪文件系统
Pseudo File System(伪文件系统) 是指没有持久化后备存储(backing store)、数据在内核中"动态生成"的文件系统。当用户 cat /proc/cpuinfo 时,不是从磁盘读文件——内核实时构造文本内容,直接返回给用户。
与磁盘文件系统(ext4 的 ext4_file_read_iter() 从 page cache 或磁盘读数据块)的本质区别:
| 维度 | 磁盘文件系统(ext4/XFS/NFS) | 伪文件系统(procfs/sysfs/cgroupfs) |
|---|---|---|
| 后备存储 | 块设备上的 inode + 数据块 | 无持久化存储,数据来自内核数据结构 |
| 读操作 | generic_file_read_iter() — 查 page cache → 缺页读磁盘 | 各自实现,通常直接格式化内核数据(如 seq_read()) |
| 写操作 | 修改 page cache + 标记脏页(回写) | 解析文本 → 修改内核变量或触发内核行为 |
| 文件大小 | 真实大小(i_size 对应磁盘块数) | 通常显示为 0(stat 看到的),但 read 能读出动态内容 |
| 挂载设备 | 有块设备(/dev/sda1) | nodev(无设备),直接用文件系统类型名挂载 |
一句话定位:伪文件系统的本质是把内核数据结构映射成文件。读写文件 = 读写内核变量/触发内核行为,避免了为每种数据格式发明新的系统调用。
二、分类全景
内核提供的伪文件系统有十几个,按用途分六大类:

Fig 2.1:内核伪文件系统六大分类。绿色=信息获取,蓝色=调试追踪,橙色=资源控制,紫色=内存存储,红色=设备节点,灰色=专用场景。
三、逐个详解
3.1 procfs(进程文件系统)—— /proc
挂载点:/proc(mount -t proc proc /proc)
一句话:procfs 是 Linux 最早的伪文件系统(1993 年随 Linux 0.99 引入),最初设计目的是提供进程信息,后来膨胀为内核统计数据的"总目录"。
内容可分为三大类:
| 类别 | 典型路径 | 内容 |
|---|---|---|
| 进程级 | /proc/<pid>/ | status(状态/内存)、maps(地址映射)、fd/(打开文件)、stack(内核栈)、io(I/O 统计)、sched(调度详情)、stat/statm(资源使用)、oom_score/oom_adj(OOM 评分) |
| 系统级(只读) | /proc/ | cpuinfo(CPU 信息)、meminfo(系统内存统计)、stat(整体 CPU/中断/上下文切换统计)、loadavg(负载)、diskstats(磁盘 I/O)、vmstat(虚拟内存)、zoneinfo(NUMA zone 详情)、buddyinfo(伙伴系统)、slabinfo(slab 分配器) |
| 系统级(可写) | /proc/sys/ | 即 sysctl 接口——/proc/sys/kernel/、/proc/sys/net/、/proc/sys/vm/ 等,可通过 sysctl 命令或直接 echo 修改内核参数 |
实现方式:大多数 /proc 文件使用 seq_file 接口(seq_read()),按需动态生成文本。
性能视角:
top/vmstat/pidstat/sar/perf stat等工具的数据源几乎都是/proc。频繁open()/read()/close()/proc/<pid>/stat文件会产生可观的系统调用开销——每次访问都触发一次"内核构造文本→拷贝到用户态"的往返,这正是pidstat采样间隔不能太短的底层原因。
3.2 sysfs —— /sys
挂载点:/sys(mount -t sysfs sysfs /sys)
一句话:sysfs 是 Linux 2.6 引入的(2003 年),专门暴露内核对象(kobject)拓扑——设备树、驱动、总线、电源管理、NUMA 节点。与 procfs 的"进程视角"不同,sysfs 是"设备/驱动视角"。
内容层次:
| 目录 | 内容 |
|---|---|
/sys/devices/ | 所有设备的物理拓扑树(真实的父子/总线关系) |
/sys/bus/ | 按总线类型索引(pci/、usb/、scsi/ 等),含驱动和设备链接 |
/sys/class/ | 按功能分类(net/、block/、thermal/ 等) |
/sys/block/ | 块设备属性(sda/、nvme0n1/ 的队列深度、调度器、统计) |
/sys/devices/system/ | CPU、内存、NUMA 节点等系统级设备 |
/sys/kernel/ | 内核子系统入口(mm/、tracing/、debug/ 等) |
/sys/fs/ | 文件系统相关(cgroup/、fuse/ 等) |
与 procfs 的分工:
| 维度 | procfs | sysfs |
|---|---|---|
| 视角 | 进程/系统行为统计 | 设备/内核对象属性 |
| 典型内容 | CPU 使用率、内存占用、进程状态 | 设备树、驱动绑定、NUMA 拓扑 |
| 数据样式 | 多数是多字段单行文本、部分多行文本 | 多数是单值文件(cat 返回一个数字/字符串) |
| 是否可写 | /proc/sys/ 可写,其余只读 | 大量文件可写(控制设备行为) |
性能视角:sysfs 中的
/sys/devices/system/node/是获取 NUMA 拓扑的关键入口——numactl --hardware从这里读取 CPU-内存距离矩阵和节点内存大小。/sys/block/sda/queue/则是调优磁盘队列参数(scheduler、nr_requests、read_ahead_kb)的控制界面。
3.3 debugfs —— /sys/kernel/debug
挂载点:/sys/kernel/debug(mount -t debugfs none /sys/kernel/debug)
一句话:debugfs 是为内核开发者提供**"随意导出调试数据"**的通道——不承诺稳定的 ABI(Application Binary Interface,应用二进制接口),格式随时可改,因此可以大胆暴露内核内部状态。
与 procfs/sysfs 的区别:
| 维度 | procfs / sysfs | debugfs |
|---|---|---|
| ABI 承诺 | 稳定——用户态程序依赖其格式,不能随意改动 | 无承诺——格式/路径随时可改,甚至整个文件消失 |
| 目标用户 | 系统工具(top、systemd、udev) | 内核开发者、高级调试者 |
| 挂载 | 默认挂载 | 多数发行版需手动挂载 |
| 内容 | 受约束的、经过审查的数据 | 任意内核内部状态 |
典型内容:
| 子系统 | 路径 | 内容 |
|---|---|---|
| 块层 | debugfs/block/<dev>/ | 块设备 IO 调度器详细统计 |
| ext4 | debugfs/ext4/<dev>/ | 文件系统内部状态 |
| 内存管理 | debugfs/mm/ | 内存碎片、水位线 |
| BPF | debugfs/bpf/ | BPF 程序/映射调试 |
| 调度 | debugfs/sched/ | 调度器内部统计 |
| 无线 | debugfs/ieee80211/ | WiFi 驱动调试信息 |
注意:在新版内核中,ftrace 等追踪功能已从 debugfs 迁移到独立的 tracefs(见 3.4),debugfs 逐渐回归"纯调试"定位。
3.4 tracefs —— /sys/kernel/tracing
挂载点:/sys/kernel/tracing(mount -t tracefs none /sys/kernel/tracing)
一句话:tracefs 是 Linux 4.1 引入的(2015 年),从 debugfs 中剥离出 ftrace 的追踪接口,给它一个独立的文件系统。这样即使 debugfs 未挂载,ftrace 仍可用。
控制 VS 输出:
| 目录 | 用途 | 典型操作 |
|---|---|---|
/sys/kernel/tracing/ 根目录 | 全局控制文件 | current_tracer(选择追踪器)、tracing_on(开关)、trace(输出)、trace_pipe(实时流式输出)、set_ftrace_filter(过滤函数) |
events/ | 事件追踪点 | events/sched/、events/syscalls/、events/block/ ——每个子目录有 enable 文件 |
instances/ | 追踪实例 | 创建独立的 trace buffer,互不干扰 |
per_cpu/ | 每个 CPU 的 trace 快照 | 多核系统的分核 trace 数据 |
性能视角:ftrace 之所以能做到"几乎零开销",就是因为 tracefs 只是一个控制界面——真正的追踪逻辑在
__trace_printk()等内核函数中直接写 per-CPU ring buffer,不涉及文件 I/O。只有当用户cat trace时,内核才把 ring buffer 的内容格式化为文本输出。详情见 ftrace-guide.md。
3.5 cgroupfs / cgroup2fs —— /sys/fs/cgroup
挂载点:/sys/fs/cgroup(v1 内部按子系统分别挂载,v2 统一挂载为 cgroup2)
一句话:cgroupfs 把进程的资源限制(CPU、内存、IO、PID 数量等)暴露为文件操作——创建子目录 = 创建 cgroup,向文件写入数值 = 设置 limit,把 PID 写入 cgroup.procs = 将进程加入控制组。
v1 与 v2 的关键差异:
| 维度 | cgroup v1 | cgroup v2 |
|---|---|---|
| 层级模型 | 每个 subsystem 独立树(可不同) | 统一树(所有 subsystem 共享同一层级) |
| 文件接口 | 每个 subsystem 各自定义 | 统一接口(cgroup.controllers、cgroup.subtree_control) |
| 线程模式 | 无原生支持 | 支持 threaded 模式(cgroup.type: threaded) |
| 内存接口 | memory.limit_in_bytes、memory.usage_in_bytes | memory.max、memory.current |
| CPU 接口 | cpu.cfs_quota_us、cpu.cfs_period_us | cpu.max |
典型文件操作:
bash
# 创建一个叫 myapp 的 cgroup
mkdir /sys/fs/cgroup/myapp
# 限制其最多使用 1 个 CPU
echo "100000 100000" > /sys/fs/cgroup/myapp/cpu.max
# 将进程 12345 加入该 cgroup
echo 12345 > /sys/fs/cgroup/myapp/cgroup.procs性能视角:容器(Docker/Kubernetes)的资源限制底层就是通过 cgroupfs 实现的。频繁修改 cgroup 文件(如弹性伸缩场景)的开销来自两个层面——(1) 文件系统操作本身的 syscall 开销,(2) 内核重新计算调度/内存回收参数的开销。详情见 /concepts/process/task-resources/namespaces-cgroups.md。
3.6 tmpfs / ramfs / hugetlbfs —— 纯内存文件系统
这三个文件系统的共同点:数据只存在于 RAM 中,断电即消失。但它们的语义和适用场景有重要区别:
| 维度 | tmpfs | ramfs | hugetlbfs |
|---|---|---|---|
| 是否可换出 | 是——内存紧张时数据可换出到 swap | 否——永远驻留 RAM | 否——使用大页(2MB/1GB),不换出 |
| 大小限制 | 有上限(size= 挂载选项) | 无上限——写多少吃多少内存 | 受系统大页池限制 |
| 典型挂载点 | /dev/shm、/tmp、/run | 无默认挂载点 | /dev/hugepages |
| 典型用途 | POSIX 共享内存、/tmp、容器层 | 高性能临时数据(但需严格限制大小) | DPDK、数据库大页内存映射、虚拟机 VFIO |
| 安全风险 | 使用 swap 后可接受 | 危险——失控可能导致 OOM Killer | 受限于预分配的大页数量 |
与磁盘文件系统的根本区别:tmpfs 的写操作不经过块层——数据直接进 page cache 并标记为"不可回收"(除非有 swap),不存在 pdflush 回写线程等待 I/O 完成的路径。
性能视角:
/dev/shm是tmpfs的标准挂载点,常用于无锁 IPC(共享内存)。它的read()/write()就是内存拷贝——没有任何磁盘 I/O,延迟取决于 CPU cache 和 NUMA 节点距离。
3.7 devtmpfs / devpts —— 设备节点文件系统
devtmpfs(/dev):
| 特性 | 说明 |
|---|---|
| 作用 | 内核启动时自动在 /dev 创建设备节点,不必等 udev |
| 核心逻辑 | 设备注册 → 内核触发 devtmpfs_create_node() → /dev 下出现对应文件 |
| 与 udev 的关系 | devtmpfs 提供基础设备节点,udev 在此基础上做权限、符号链接、额外属性 |
devpts(/dev/pts):
| 特性 | 说明 |
|---|---|
| 作用 | 管理伪终端(pseudo-terminal)的从设备 |
| 核心逻辑 | 每打开一个终端窗口或 SSH 连接,devpts 创建一个 pts/0、pts/1 ... |
| 多实例 | 支持 newinstance 挂载选项,容器可以有独立的 pts 命名空间 |
3.8 其他专用伪文件系统
| 文件系统 | 挂载点 | 用途 |
|---|---|---|
| configfs | /sys/kernel/config | 用户态通过 mkdir/rmdir 创建和删除内核对象(如 iSCSI target、USB gadget),比 sysfs 更适合"创建/删除"语义 |
| securityfs | /sys/kernel/security | LSMs(Linux Security Modules,Linux 安全模块)的配置接口(SELinux、AppArmor、IMA 等),不保证 ABI 稳定 |
| pstore | /sys/fs/pstore | 内核崩溃(panic/oops)时把最后一段日志写到持久化存储(ACPI ERST、UEFI 变量、RAM),重启后可读取 |
| bpf | /sys/fs/bpf | 持久化 BPF 程序和 BPF map(映射)——bpftool 和 libbpf 用 pin 操作把 BPF 对象固定到文件系统路径,挂载在此的文件系统就是 bpf |
四、伪文件系统与 VFS 的关系——统一接口下的多态实现
所有伪文件系统都构建在 VFS 框架之上,各自实现 file_operations:

Fig 4.1:六种主要伪文件系统对 VFS
file_operations的实现。虽然都是open()/read()/write(),但底层行为完全不同。这就是 VFS 多态分发的威力——用户态只认 fd,不关心底层是 ext4 还是 procfs。
各文件系统使用的内核辅助机制:
| 文件系统 | 主要内部接口 | 说明 |
|---|---|---|
| procfs | seq_file(seq_open / seq_read / seq_printf) | 适合多行文本输出,自动分页,避免一次输出过长 |
| sysfs | sysfs_ops(show / store) | 适合单值文件,一个属性一个文件 |
| debugfs | debugfs_create_file() + 自定义 fops | 最灵活——完全自定义 read/write 回调 |
| tracefs | 自定义 + tracing_fops | 专用接口,直接操作 ring buffer |
| cgroup | cgroup_file_operations | 统一框架,支持序列化和层级继承 |
五、性能工程师视角——哪些是关键数据源
从日常性能分析的频率来看,以下是使用最多的伪文件系统路径:
5.1 CPU 相关
| 路径 | 内容 | 使用工具 |
|---|---|---|
/proc/stat | 全局 CPU 时间(user/nice/system/idle/iowait/irq/softirq/steal)、中断数、上下文切换 | top、mpstat、vmstat、perf stat |
/proc/cpuinfo | CPU 型号、频率、flags、cache 大小 | lscpu、perf list |
/proc/loadavg | 1/5/15 分钟负载 | top、uptime |
/proc/<pid>/stat | 进程 CPU 时间、状态、优先级 | pidstat、top |
/proc/<pid>/sched | 进程调度详情(vruntime、睡眠时间等) | 手动 cat 调试调度问题 |
/proc/schedstat | 全局调度统计(运行队列、负载均衡) | 高级调度分析 |
/sys/devices/system/cpu/ | CPU 拓扑、online/offline、cpufreq | cpupower、lscpu -p |
/sys/kernel/debug/sched/ | 调度器内部调试信息 | 调度器开发者 |
5.2 内存相关
| 路径 | 内容 | 使用工具 |
|---|---|---|
/proc/meminfo | 系统全局内存统计(MemTotal/Free/Available、Cached、Buffers、SwapTotal/Free、AnonPages) | free、vmstat、top |
/proc/<pid>/status | 进程内存(VmRSS/VmSize)、信号屏蔽 | pidstat -r |
/proc/<pid>/smaps | 进程每段映射的 RSS/PSS 详情 | 内存泄漏排查 |
/proc/vmstat | 虚拟内存事件计数(page fault、compaction、kswapd、回收) | vmstat、sar -B |
/proc/buddyinfo | 伙伴系统各 order 的空闲块分布 | 内存碎片分析 |
/proc/slabinfo | slab 分配器使用情况 | slabtop |
/proc/zoneinfo | 每个 NUMA zone 的详细水位线、保护值 | NUMA 内存分析 |
/proc/<pid>/numa_maps | 进程每段映射的 NUMA 分布 | NUMA 亲和性检查 |
5.3 I/O 相关
| 路径 | 内容 | 使用工具 |
|---|---|---|
/proc/diskstats | 每个块设备的读写 I/O 次数、扇区数、耗时 | iostat、sar -d |
/proc/<pid>/io | 进程的读/写字节数、syscall 次数 | pidstat -d |
/sys/block/<dev>/stat | 单盘 I/O 统计(与 diskstats 对应) | 手动监控 |
/sys/block/<dev>/queue/ | 磁盘调度器、队列深度、预读取等参数 | tuned、手动调优 |
5.4 网络相关
| 路径 | 内容 | 使用工具 |
|---|---|---|
/proc/net/dev | 每个网卡的收发包数、字节数、错误数 | sar -n DEV |
/proc/net/snmp + netstat | TCP/UDP/IP 协议层统计 | netstat -s |
/proc/net/sockstat | socket 对象总数(TCP、UDP、RAW) | 排查 fd 泄漏 |
关键认知:你用的几乎所有性能工具——
top、vmstat、iostat、mpstat、sar、pidstat、free——它们展示的数据,都来自上述伪文件系统中的文件。性能工具的本质是"定时采样 → 解析文本 → 计算差值 → 格式化输出"。理解了数据源,你就理解了工具输出为什么会有"不准"的时刻(采样间隔内的事件会被漏掉)。
六、一句话总结
内核伪文件系统是 Linux"一切皆文件"哲学在运行时接口层的延伸——从 procfs 的进程统计、sysfs 的设备拓扑、debugfs/tracefs 的调试追踪、cgroupfs 的资源控制到 tmpfs/hugetlbfs 的纯内存存储,每个文件系统各自实现 file_operations,统一挂在 VFS 框架下,让用户态用 cat / echo / open() / mmap() 这些最基础的操作就能读取系统状态、修改内核参数、配置资源限制——不需要为每一种内核能力发明新的系统调用。