第一章 内核源码结构解析:从根目录到驱动代码
说实话,我第一次完整下载 Linux 内核源码时,看着那一堆目录是有点懵的。几万个文件,上千个目录,从哪看起?后来我慢慢摸清了门道——内核源码的组织方式其实非常清晰,它遵循着「子系统分离、架构隔离、配置驱动」的原则。今天我就带你走一遍这个结构,让你以后拿到任何版本的内核,都能快速找到你想要的东西。
1.1 顶层目录:一眼看穿全局
先看根目录。你执行 ls -l 后,会看到大约 20 来个顶层目录。我习惯把它们分成三类:核心子系统、架构相关、工具和构建。
| 目录名 | 归属类别 | 里面有什么 |
|---|---|---|
kernel/ |
核心子系统 | 进程调度、信号、任务管理、时间子系统 |
mm/ |
核心子系统 | 内存管理:页分配、slab、虚拟内存、交换 |
fs/ |
核心子系统 | 所有文件系统实现:ext4、btrfs、proc、sysfs 等 |
net/ |
核心子系统 | 网络协议栈:TCP/IP、socket、设备层 |
drivers/ |
核心子系统 | 所有设备驱动——这是最大的目录,没有之一 |
arch/ |
架构相关 | 按 CPU 架构分:x86、arm、arm64、riscv、mips 等 |
include/ |
公共头文件 | 内核 API 头文件,分 linux/、asm-generic/、uapi/ |
scripts/ |
构建工具 | Kconfig 解析器、模块工具、DT 编译脚本 |
Documentation/ |
文档 | 内核文档,强烈建议你多翻翻 |
arch/ 下有没有我用的平台,然后去 drivers/ 下找对应子目录。如果找不到,再去 Documentation/devicetree/ 翻绑定文档。这能省下大量时间。
1.2 核心子系统源码分布:代码在哪?
每个子系统内部都有自己的一套组织逻辑。我挑几个重点说说。
进程调度 —— kernel/sched/
调度器代码全在 kernel/sched/ 下。核心文件是 core.c,CFS 调度算法在 fair.c,实时调度在 rt.c。我记得有一次调优嵌入式设备的响应延迟,就是在 fair.c 里改了一个调度粒度参数,效果立竿见影。
内存管理 —— mm/
这个目录很直观:mmap.c 管内存映射,slab.c 管内核对象缓存,page_alloc.c 管物理页分配。你如果做驱动开发,最常打交道的是 slab.c 和 vmalloc.c。
文件系统 —— fs/
每个文件系统一个子目录。比如 fs/ext4/、fs/btrfs/。虚拟文件系统层(VFS)的核心代码在 fs/ 根目录下,namei.c、inode.c、dcache.c 这几个文件你值得记住。
网络协议栈 —— net/
按协议分层:net/ipv4/、net/ipv6/、net/core/(通用网络层)。net/sched/ 是流量控制,做嵌入式网络设备时经常要改这里。
设备驱动 —— drivers/
这是内核里最大的目录,没有之一。它按设备类型分:drivers/gpio/、drivers/i2c/、drivers/spi/、drivers/net/、drivers/mmc/ 等等。每个子目录里通常有 Kconfig 和 Makefile,控制这个驱动怎么编译。
drivers/ 根目录下加新驱动。一定要按设备类型放到对应子目录里。我曾经见过有人把 SPI 驱动扔到 drivers/misc/ 下,结果维护时找都找不到。
1.3 Makefile 与 Kconfig 体系:内核的「开关面板」
内核的构建系统,说白了就是一套「配置-编译」的流水线。Kconfig 定义选项,Makefile 执行编译。这两者配合,决定了你的内核里有什么、没有什么。
Kconfig:定义「能不能用」
每个子目录下都有一个 Kconfig 文件。它定义了这个目录下有哪些配置选项。比如 drivers/spi/Kconfig 里会有:
config SPI_IMX
tristate "Freescale i.MX SPI controller"
depends on ARCH_MXC || COMPILE_TEST
help
This enables support for the Freescale i.MX SPI controller.
这里 tristate 表示可以编入内核(Y)、编译成模块(M)、或不编译(N)。depends on 是依赖关系——你选了 i.MX 的 SPI 驱动,前提是你选了 i.MX 平台。
Makefile:决定「怎么编译」
对应的 Makefile 里写的是:
obj-$(CONFIG_SPI_IMX) += spi-imx.o
这行代码的意思是:如果 CONFIG_SPI_IMX 被设为 y,就把 spi-imx.c 编进内核;如果设为 m,就编成模块;如果没设,就不编译。
核心逻辑:Kconfig 定义「有什么选项」,Makefile 根据选项的值决定「编什么文件」。你运行 make menuconfig 时看到的界面,就是 Kconfig 文件渲染出来的。
1.4 一张图看懂内核源码结构
下面这张图是我自己整理的,把内核源码的顶层结构、子系统分布、构建流程串在了一起。你看完应该能对内核源码有个整体印象。
1.5 几个实用技巧
最后分享几个我平时用的小技巧,希望能帮你少走弯路。
- 找函数定义:用
grep -r "function_name" --include="*.c" --include="*.h"。我习惯在include/linux/下先搜头文件声明,再去kernel/或mm/下找实现。 - 看配置依赖:在
Kconfig文件里搜depends on,能快速理清一个功能依赖哪些其他配置。我曾经在配置 SPI 驱动时,发现它依赖GPIO和CLK,少选一个就编不过。 - 快速定位驱动:如果你知道设备树 compatible 字符串,直接在源码里搜它,就能找到对应的驱动文件。比如搜
"fsl,imx6ul-uart",马上定位到串口驱动。 - 理解编译粒度:一个驱动可以编进内核(Y)、编成模块(M)、或不编(N)。做嵌入式开发时,我建议把不常用的驱动都编成模块,启动快,调试也方便。
depends on 依赖的选项没开。记住:Kconfig 的依赖链是传递的,A 依赖 B,B 依赖 C,那 A 就间接依赖 C。用 make menuconfig 搜索功能(按 / 键)可以快速查依赖关系。
好了,内核源码的结构就讲到这里。你只要记住:核心代码在 kernel/、mm/、fs/、net/、drivers/ 里,架构相关在 arch/ 下,构建配置靠 Kconfig + Makefile 配合。有了这个框架,后面我们讲具体配置和驱动调优时,你就能快速找到对应的代码位置了。