文件系统与调试基础
File System in Linux
-
提供非易失性数据的存储空间。
-
一种特定的数据存储格式,包含两部分:数据本身(Data)和元数据(Meta-data)。
-
每种文件系统类型使用自身的元数据结构来定义数据的存储和访问方式。
-
支持所有文件操作。
-
一些最常用的文件系统包括:EXT4、EXT3、FAT-32、NTFS。
graph LR subgraph UserSpace[用户空间] App[Application] end subgraph KernelSpace[内核空间] SysCall[System Call] VFS[Virtual File System] EXT3[EXT3] EXT4[EXT4] FAT32[FAT-32] NTFS[NTFS] end subgraph HardwareLayer[硬件层] Hardware[Hardware] end App --> SysCall SysCall --> VFS VFS --> EXT3 VFS --> EXT4 VFS --> FAT32 VFS --> NTFS EXT3 --> Hardware EXT4 --> Hardware FAT32 --> Hardware NTFS --> Hardware style UserSpace fill:#e1f5fe,stroke:#01579b style KernelSpace fill:#fff3e0,stroke:#e65100 style HardwareLayer fill:#e8f5e9,stroke:#1b5e20
Disadvantage of printk as debugging tool
- 修改源代码可能并非易事,也无法快速完成。
- 不必要的 printk 可能会影响整体性能。
- 你需要维护两个版本的软件;一个包含调试信息,另一个不包含。
- 有时,printk 会掩盖实际的问题。
- 在大多数情况下,获取信息的最佳方式是查询系统。
procFS 概述
What is proc
- 通过查询从内核及内核模块获取信息的技术之一。
/proc文件系统是一个特殊的、由软件创建的文件系统。- 提供了一个用于在用户空间和内核空间之间进行通信的接口。
- 它包含关于当前正在运行的进程的有用信息,例如
/proc/modules– 提供模块列表/proc/meminfo– 统计内存使用情况
procFS vs other File Systems
- proc 是虚拟文件系统或伪文件系统。
- 数据不存储在非易失性存储器中。
- 它没有实际的文件。
- proc 没有像标准文件系统那样的数据存储格式。
- 文件系统独立于操作系统。然而,/proc 并非在所有操作系统中都可用。
Importance of procFS
-
Maintains information about the currently running system
1 2 3 4 5 6 7 8 9charles@ubuntu24:~$ cat /proc/meminfo MemTotal: 1995676 kB MemFree: 698620 kB MemAvailable: 1642032 kB Buffers: 59008 kB Cached: 993020 kB SwapCached: 0 kB Active: 139892 kB Inactive: 939312 kB -
Useful tool to monitor the system
1 2 3 4 5charles@ubuntu24:~$ cat /proc/net/route Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT enp0s3 00000000 0202000A 0003 0 0 100 00000000 0 0 0 enp0s3 0002000A 00000000 0001 0 0 100 00FFFFFF 0 0 0 charles@ubuntu24:~$ cat /proc/net/nf_conntrack tcp 6 431999 ESTABLISHED src=192.168.1.10 dst=93.184.216.34 sport=54321 dport=80 src=93.184.216.34 dst=192.168.1.10 sport=80 dport=54321 [ASSURED] mark=0 use=1 -
It is used to configure and debug the system.
/proc/sysdoes that1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23charles@ubuntu24:~$ cat /proc/sys/kernel/printk 4 4 1 7 charles@ubuntu24:~$ echo 7 > /proc/sys/kernel/printk charles@ubuntu24:~$ cat /proc/sys/kernel/printk 7 4 1 7 charles@ubuntu24:~$ cat /proc/sys/net/ipv4/ip_forward 0 charles@ubuntu24:~$ echo 1 > /proc/sys/net/ipv4/ip_forward charles@ubuntu24:~$ cat /proc/sys/net/ipv4/ip_forward 1 charles@ubuntu24:~$ ls -l /proc/80094/fd total 0 lrwx------ 1 root root 64 Jul 9 14:32 0 -> /dev/null lrwx------ 1 root root 64 Jul 9 14:32 1 -> /dev/null lr-x------ 1 root root 64 Jul 9 14:32 10 -> 'pipe:[85474]' lr-x------ 1 root root 64 Jul 9 14:32 12 -> 'pipe:[85475]' lrwx------ 1 root root 64 Jul 9 14:32 2 -> /dev/null lrwx------ 1 root root 64 Jul 9 14:32 3 -> 'socket:[84325]' lrwx------ 1 root root 64 Jul 9 14:32 4 -> 'socket:[84293]' lrwx------ 1 root root 64 Jul 9 14:32 5 -> 'socket:[85467]' lrwx------ 1 root root 64 Jul 9 14:32 6 -> 'socket:[85442]' l-wx------ 1 root root 64 Jul 9 14:32 8 -> 'pipe:[85473]' l-wx------ 1 root root 64 Jul 9 14:32 9 -> /run/systemd/sessions/8.ref
Creating a proc file and interfacing with user space
-
Creating entry in
/proc
Entry can be created usingproc_create()
ent = proc_create("dummydev", 0660, NULL, &myops); -
Removing entry from
/proc
Entry can be removed usingproc_remove()proc_remove(ent); -
Handlers of proc file
Two handlers; read and write.1 2 3 4 5 6static struct file_operations myops = { .owner = THIS_MODULE, .read = dummy_read, .write = dummy_write, } -
Communicating with driver using proc
can use shell or program
|
|
|
|
|
|
|
|
|
|
SysFS 概述
What is sysfs
sysfs 是一个挂载在 /sys 下的内存文件系统(RAM-based),它把内核内部的对象模型(设备、驱动、总线、内核子系统等)以“目录 + 文件”的形式导出到用户空间。相比 procFS,它更加结构化、规范化。
它的核心价值在于:
- 一个属性一个文件:每个文件通常只表示一个值(one value per file),便于 shell、脚本读写;
- 树形结构反映内核对象关系:目录层级对应内核对象(kobject)的父子/从属关系;
- 替代
/proc的滥用:早期很多设备信息塞进/proc,sysfs 提供了结构化、规范化的替代方案。
什么是内存文件系统(RAM-based)
“内存文件系统”指的是:文件系统的数据结构和内容完全存在于内存(RAM)中,不落盘到任何块设备(硬盘、SSD、U 盘)上。
核心特征:
- 无后端存储介质:普通文件系统(EXT4、NTFS、FAT32)的数据最终写到磁盘等非易失存储上,掉电后仍在;内存文件系统只活在 RAM 里,掉电/重启即消失。
- 内容通常是“动态生成”的,而非“存储”的:对于 sysfs / procFS 这类特殊内存文件系统,文件里的内容甚至不是提前存好的字节,而是你
cat的那一刻由内核回调函数临时生成的。例如cat /sys/kernel/sysfs_demo/foo会触发内核调用foo_show()→sprintf(buf, "%d\n", foo),把当前内核变量foo的值现算现返回。 - 没有真正的磁盘布局/元数据格式:procFS/sysfs “没有实际的文件”,也“没有像标准文件系统那样的数据存储格式”,只是借用了 VFS 的“文件/目录”接口外壳,底层数据来自内核对象(sysfs 用 kobject + kernfs)。
内存文件系统其实有两种典型形态:
| 类型 | 例子 | 内容来源 | 用途 |
|---|---|---|---|
| 数据型内存 FS | tmpfs、ramfs |
真的把你写入的文件字节存在 RAM 里 | 当作高速临时磁盘用(如 /tmp、/dev/shm) |
| 接口型内存 FS(伪文件系统) | procfs、sysfs、debugfs |
内容由内核动态生成,是内核状态的“视图” | 用户空间 ↔ 内核空间通信 |
sysfs 属于第二类:它不是用来存文件的,而是把内核对象模型(设备、驱动、总线等)以目录+文件的形式“投影”出来,给用户空间一个读写内核状态的规范接口。
一句话总结:内存文件系统 = 只存在于 RAM、掉电即失的文件系统;而 sysfs 更进一步,连内容都不真正“存储”,而是内核在你访问的瞬间实时生成的一层结构化视图。
sysfs vs procFS
| 维度 | procFS (/proc) |
sysfs (/sys) |
|---|---|---|
| 定位 | 进程信息 + 早期的杂项内核接口 | 内核对象模型(设备/驱动/总线)的结构化视图 |
| 组织方式 | 较随意,一个文件可含多行多字段 | 规范:一个文件一个值 |
| 底层实现 | proc_fs / seq_file | kobject + kernfs |
| 常见用途 | 状态查询、/proc/sys 调参 |
设备属性导出、驱动调参与状态 |
常见的 /sys 顶层目录:
| 目录 | 含义 |
|---|---|
/sys/kernel |
内核自身的可调参数、子系统入口 |
/sys/devices |
系统中所有设备的全局设备树 |
/sys/class |
按“功能类别”组织的设备视图(如 net、block) |
/sys/bus |
按总线组织(如 pci、usb、platform) |
/sys/module |
已加载模块及其参数 |
sysfs 核心原理:kobject + attribute + kernfs
sysfs 本身几乎不存放数据,它是内核对象模型的“视图层”。要理解它,需要抓住三个关键抽象。
kobject —— 目录
kobject 是 sysfs 目录的载体。每一个出现在 /sys 里的目录,背后基本都对应一个 kobject(源码:include/linux/kobject.h)。
|
|
关键点:
parent决定该目录挂在谁下面;sd(sysfs directory)指向底层的kernfs_node,这是真正在虚拟文件系统里存在的节点;kref是引用计数,kobject_get/put增减引用,归零时调用ktype->release释放。
attribute —— 文件
目录里的“文件”由 struct attribute 描述,它只声明文件名和权限,本身不含读写逻辑(源码:include/linux/sysfs.h)。
|
|
sysfs_ops / kobj_type —— 读写逻辑的分发
真正的读写行为绑定在 kobj_type 上。它把 attribute 与一组 sysfs_ops 关联起来(源码:include/linux/kobject.h)。
|
|
当用户态 cat 一个 sysfs 文件时,VFS → kernfs → sysfs_ops->show(),再由 show 找到对应的 attribute 并调用具体处理函数;echo > file 则走 store()。
kobj_attribute —— 最常用的“文件 + 处理函数”打包
对于挂在 /sys/kernel 这类简单场景,内核提供了现成的 struct kobj_attribute,把 attribute 和 show/store 直接绑在一起(源码:include/linux/kobject.h)。
|
|
配合 __ATTR 宏可以一行完成定义(源码:include/linux/sysfs.h)。
|
|
注意
VERIFY_OCTAL_PERMISSIONS:sysfs 属性不允许 world-writable,否则编译期就会报错。
一张图理清调用链
|
|
写方向(echo 5 > foo)完全对称,走 store()。
常用 API 速查
| API | 作用 |
|---|---|
kobject_create_and_add(name, parent) |
创建一个 kobject 目录并加入 sysfs |
sysfs_create_file(kobj, attr) |
在目录下创建单个文件 |
sysfs_create_group(kobj, group) |
一次性创建一组文件 |
sysfs_remove_group(kobj, group) |
移除一组文件 |
kobject_put(kobj) |
减少引用,归零后销毁目录 |
attribute_group 用于批量管理属性,若指定 .name 会额外生成一层子目录(源码:include/linux/sysfs.h)。
Creating a sysfs file and interfacing with user space
Linux 源码本身就自带一个非常好的示例:samples/kobject/kobject-example.c。它会在 /sys/kernel/kobject_example/ 下创建 foo、baz、bar 三个可读写文件。
下面基于它给出一个精简、可独立编译的版本,并附带构建/运行步骤。
|
|
|
|
|
|
编写 sysfs 属性的注意事项
- 一个文件一个值:不要在一个文件里塞多个字段,保持简单可解析。
show的 buf 大小是一个 PAGE_SIZE:写入内容不要超过一页(通常 4KB),用sysfs_emit()(新内核)或sprintf时注意边界。store返回值语义:返回“已消费字节数”(通常是count),或返回负的错误码;返回 0 会导致用户态无限重试。- 权限不能 world-writable:
__ATTR里的 mode 若包含0002会被VERIFY_OCTAL_PERMISSIONS拒绝编译。 - 生命周期用引用计数管理:目录的销毁靠
kobject_put(),不要手动去删底层文件。 - 动态命名对象用
kobject_init_and_add+ uevent:kobject_create_and_add适合名字固定的简单目录,不会发送 uevent。
小结
- sysfs = 内核对象模型(kobject)在用户空间的结构化视图;
- 目录 = kobject,文件 = attribute,读写逻辑 = sysfs_ops/kobj_attribute;
- 底层由 kernfs 提供虚拟文件系统能力,kobject 通过
sd挂接; - 用
kobject_create_and_add+sysfs_create_group即可几十行代码导出一个可读写接口,非常适合驱动调参与状态导出。
想进一步深入,可阅读本仓库文档 Documentation/filesystems/sysfs.txt 以及 samples/kobject/ 下的两个官方示例。