第一章 内核源码结构解析:从根目录到驱动代码

说实话,我第一次完整下载 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 一张图看懂内核源码结构

下面这张图是我自己整理的,把内核源码的顶层结构、子系统分布、构建流程串在了一起。你看完应该能对内核源码有个整体印象。

Linux 内核源码顶层结构 Linux 内核源码根目录 核心子系统 架构相关 构建与工具 kernel/ 调度 mm/ 内存管理 fs/ 文件系统 net/ 网络栈 drivers/ 驱动 arch/x86/ arch/arm/ arch/arm64/ arch/riscv/ scripts/ Makefile Kconfig Documentation/ 构建流程:Kconfig → Makefile → 编译 1. 运行 make menuconfig,读取各目录 Kconfig,生成 .config 2. 顶层 Makefile 包含 .config,将配置变量传递给子目录 Makefile 3. 子目录 Makefile 根据 obj-y / obj-m 决定编译哪些 .c 文件 驱动开发重点关注 • drivers/ 下按设备类型找对应子目录 • 每个驱动目录都有 Kconfig + Makefile • include/linux/ 下找内核 API 头文件

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)。做嵌入式开发时,我建议把不常用的驱动都编成模块,启动快,调试也方便。
避坑指南:我曾经在移植内核时,发现某个驱动死活编不进去。查了半天,原来是 Kconfig 里有个 depends on 依赖的选项没开。记住:Kconfig 的依赖链是传递的,A 依赖 B,B 依赖 C,那 A 就间接依赖 C。用 make menuconfig 搜索功能(按 / 键)可以快速查依赖关系。

好了,内核源码的结构就讲到这里。你只要记住:核心代码在 kernel/、mm/、fs/、net/、drivers/ 里,架构相关在 arch/ 下,构建配置靠 Kconfig + Makefile 配合。有了这个框架,后面我们讲具体配置和驱动调优时,你就能快速找到对应的代码位置了。