1. Linux 内核源码结构:各目录到底管什么

很多初学者拿到内核源码,第一反应就是懵。几万个文件,上百个目录,从哪看起?

我刚开始接触内核时也这样。那时候编译一次内核要等半小时,改个驱动还得满世界找头文件。后来慢慢摸清了门道——内核源码的目录结构,其实是有逻辑的。你只要搞懂每个顶层目录的职责,就能像查字典一样快速定位代码。

下面这张图,是我自己整理的内核源码顶层目录关系。建议你先看一遍,再往下读。

Linux 内核源码顶层目录结构图 Linux 内核源码顶层目录结构 核心子系统 kernel/ mm/ fs/ ipc/ net/ block/ crypto/ lib/ arch/(体系结构) drivers/(驱动) sound/(音频) scripts/(构建脚本) init/(初始化) include/(头文件) Documentation/ security/(安全) virt/(虚拟化) samples/(示例) 说明:绿色=核心子系统,红色=硬件相关,紫色=构建辅助,橙色=安全/虚拟化,青色=示例

1.1 核心子系统:内核的"五脏六腑"

先看最核心的几个目录。它们构成了操作系统的骨架。

目录 职责 典型内容
kernel/ 进程管理、调度、信号、系统调用 sched.c, fork.c, signal.c
mm/ 内存管理(虚拟内存、页分配、SLAB) mmap.c, page_alloc.c, slab.c
fs/ 文件系统(VFS + 具体文件系统) ext4/, btrfs/, nfs/, sysfs/
ipc/ 进程间通信(信号量、消息队列、共享内存) sem.c, msg.c, shm.c
net/ 网络协议栈(TCP/IP、套接字、路由) ipv4/, ipv6/, bridge/, netfilter/
block/ 块设备层(I/O 调度、bio、分区) blk-core.c, elevator.c, partitions/
crypto/ 加密 API(对称/非对称、哈希、AEAD) aes.c, sha256.c, algapi.c
lib/ 内核通用库(字符串、CRC、红黑树、KASAN) string.c, rbtree.c, kobject.c
我的习惯: 调试驱动时,我经常在 kernel/ 里翻 printk.c 和 panic.c。遇到内核崩溃,先看 kernel/panic.c 里的 panic() 函数——它是一切"死机"的终点。

1.2 硬件相关目录:arch/ 和 drivers/

这两个目录,是驱动开发者的主战场。

arch/ —— 体系结构相关代码

每种 CPU 架构都有自己的子目录。比如:

  • arch/x86/ —— 桌面、服务器最常见的 x86/x86_64
  • arch/arm/ —— 嵌入式、手机用的 ARM 架构
  • arch/arm64/ —— 64 位 ARM(AArch64)
  • arch/riscv/ —— 开源指令集 RISC-V
  • arch/mips/ —— MIPS 架构(路由器、嵌入式)

每个 arch 目录下,通常包含:

  • boot/ —— 启动代码(head.S、压缩解压)
  • kernel/ —— 架构相关的核心代码(中断、时钟、信号)
  • mm/ —— 架构相关的内存管理(页表、TLB)
  • include/ —— 架构相关的头文件
  • configs/ —— 默认内核配置文件
注意: 移植内核到新板子时,arch/ 是改动最大的地方。我曾经在移植 RISC-V 内核时,光 arch/riscv/boot/ 里的设备树和启动汇编就调了一周。

drivers/ —— 设备驱动的大本营

这是内核源码中最大的目录,没有之一。按设备类型划分子目录:

  • drivers/gpio/ —— GPIO 控制器驱动
  • drivers/i2c/ —— I2C 总线及设备驱动
  • drivers/spi/ —— SPI 总线驱动
  • drivers/pci/ —— PCI/PCIe 子系统
  • drivers/usb/ —— USB 主机/设备/控制器驱动
  • drivers/net/ —— 网络设备驱动(以太网、无线)
  • drivers/mmc/ —— SD/MMC/eMMC 卡驱动
  • drivers/input/ —— 输入设备(键盘、触摸、鼠标)
  • drivers/video/ —— 显示/帧缓冲驱动
  • drivers/char/ —— 字符设备(串口、随机数、看门狗)
  • drivers/mtd/ —— 存储类设备(NAND Flash、NOR Flash)
  • drivers/staging/ —— 暂存区(未完全成熟的驱动)
避坑指南: 我曾经在调试一个 SPI 触摸屏驱动时,死活找不到对应的代码。后来发现它藏在 drivers/input/touchscreen/ 里,而不是 drivers/spi/。记住:驱动按"功能类型"归类,不是按"总线类型"。

1.3 构建与辅助目录

这些目录不直接参与内核运行,但开发过程中离不开它们。

目录 作用 我常用的场景
scripts/ 构建脚本、配置工具、分析工具 scripts/checkpatch.pl 检查代码风格
init/ 内核初始化流程(main.c、initcall) 调试启动卡住时,看 init/main.c
include/ 内核头文件(linux/, asm-generic/, uapi/) 写驱动时,#include <linux/module.h> 就在这
Documentation/ 内核文档(RST 格式、Kconfig 说明) 查 API 用法、设备树绑定、驱动编写指南
小技巧: 写驱动时不确定某个函数怎么用?先搜 Documentation/ 里有没有相关文档。比如 Documentation/driver-api/ 里就有 GPIO、I2C、SPI 的编程指南。比看源码快多了。

1.4 特殊功能目录

这几个目录,平时用得少,但关键时刻很有用。

  • security/ —— 安全模块(SELinux、AppArmor、IMA)
  • virt/ —— 虚拟化(KVM 核心代码)
  • sound/ —— ALSA 音频子系统(驱动 + 核心)
  • samples/ —— 示例代码(写驱动时参考的好地方)
  • usr/ —— initramfs 打包相关
  • firmware/ —— 固件文件(部分驱动需要)

1.5 目录之间的关联:一个例子

光说理论太枯燥。我们看一个实际场景:写一个 I2C 温度传感器驱动。

这个驱动会涉及哪些目录?

  1. drivers/i2c/ —— I2C 核心框架(注册设备、收发数据)
  2. drivers/hwmon/ —— 硬件监控子系统(温度传感器的"家")
  3. include/linux/ —— 头文件(i2c.h, hwmon.h)
  4. Documentation/ —— 查 I2C 驱动编写规范
  5. arch/arm/boot/dts/ —— 设备树文件(描述硬件连接)
  6. scripts/ —— 用 checkpatch.pl 检查代码格式

你看,一个简单的驱动,就串起了 6 个目录。这就是内核源码的"网状"结构——每个目录都不是孤岛。

核心观点: 学内核源码,不要试图记住每个文件。先记住目录的"职责边界"。遇到问题,先想"这个问题属于哪个子系统",再去对应目录里找。效率能翻倍。

1.6 我的学习建议

最后,分享几个我自己的经验:

  • 先看 init/main.c —— 这是内核的入口,从 start_kernel() 开始,一路看它调用了哪些初始化函数。看完你就知道内核启动的"骨架"了。
  • 别怕 include/ —— 很多初学者觉得头文件太多。其实 include/linux/ 里就是内核的"API 手册"。常用的 module.h, fs.h, device.h 都在那。
  • 善用 Documentation/ —— 内核文档质量参差不齐,但 Documentation/driver-api/ 和 Documentation/devicetree/ 写得不错。遇到不懂的,先搜文档,再读源码。
  • 用 scripts/ 里的工具 —— checkpatch.pl 能帮你检查代码风格,kernel-doc 能生成文档。这些工具能省不少事。

嗯,这一章就到这里。目录结构是内核源码的"地图",先记住地图,后面我们才能在里面"开车"。