3、Linux内核源码结构:内核源码目录树解析、Kconfig与Makefile机制、内核配置系统详解

说实话,刚接触Linux内核源码那会儿,我也挺懵的。几千个文件夹,几万个文件,从哪下手?

后来做BSP移植多了,慢慢摸清了门道。内核源码看着庞大,其实结构很清晰。今天我就带你把这棵「代码大树」的枝干捋一遍。

3.1 内核源码目录树:从根到叶

先看顶层目录。你下载完内核源码,ls -l 一下,会看到这些核心目录:

目录/文件 作用 我常关注的点
arch/ 体系结构相关代码 做BSP移植,90%的时间泡在这里
drivers/ 设备驱动 字符设备、块设备、网络设备都在这里
fs/ 文件系统实现 ext4、proc、sysfs等
include/ 头文件 全局宏定义、结构体声明
kernel/ 核心调度、进程管理 一般不动,除非做深度定制
mm/ 内存管理 页分配、SLAB分配器
net/ 网络协议栈 TCP/IP、Netfilter
scripts/ 编译、配置脚本 Kconfig解析器就在这里
Documentation/ 内核文档 我习惯先翻这里找线索

举个例子,你拿到一块新板子,CPU是ARM Cortex-A7。那第一件事就是进 arch/arm/ 目录。里面按厂商分:mach-xxx 是板级文件,plat-xxx 是平台代码。我当年移植TI的AM335x,就是在 arch/arm/mach-omap2/ 里折腾了好久。

我的习惯: 拿到新内核,先跑一遍 tree -L 2 看看顶层结构。心里有个地图,后面找代码快很多。

3.2 Kconfig与Makefile机制:内核的「骨架」和「肌肉」

内核不是把所有代码都编译进去的。它靠两样东西控制:Kconfig 管「要不要编译」,Makefile 管「怎么编译」。

3.2.1 Kconfig:配置选项的「说明书」

每个驱动目录下都有个 Kconfig 文件。它定义了配置项的菜单结构。看个例子:

# drivers/i2c/Kconfig
menu "I2C support"
    config I2C
        tristate "I2C bus support"
        help
          Say Y here if you want I2C support.

    config I2C_CHARDEV
        tristate "I2C device interface"
        depends on I2C
        help
          This driver provides a /dev/i2c-N interface.
endmenu

这里的关键词:

  • tristate:三态——不编译(Y)、编译进内核(y)、编译成模块(m)
  • depends on:依赖关系。I2C_CHARDEV 必须等 I2C 被选中才能出现
  • select:反向选择。比如某个驱动可以自动选上它依赖的另一个驱动

我在项目中遇到过一个问题:某个网卡驱动怎么都配不出来。后来发现是 depends on 链断了——它依赖的PHY驱动没被选上。嗯,这种坑很隐蔽。

3.2.2 Makefile:编译的「施工图」

Kconfig 决定了「要不要做」,Makefile 决定「怎么做」。同样看个例子:

# drivers/i2c/Makefile
obj-$(CONFIG_I2C)       += i2c-core.o
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o

这里的 obj-y 表示编译进内核,obj-m 表示编译成模块,obj-n 表示不编译。而 CONFIG_I2C 这个变量,就是你在 make menuconfig 里配置后生成的。

核心逻辑: Kconfig 定义选项 → 生成 .config 文件 → Makefile 根据 .config 决定编译哪些文件。这条链不能断。

3.3 内核配置系统详解:从 menuconfig 到 .config

配置系统说白了就是三个环节:

  1. 配置界面:menuconfig、xconfig、nconfig 等
  2. 配置数据库:.config 文件
  3. 配置解析器:scripts/kconfig/ 下的工具

你执行 make menuconfig,实际上调用了 scripts/kconfig/mconf 这个程序。它读取所有 Kconfig 文件,生成一个菜单树。你选完保存,就写入了 .config。

我建议你养成一个习惯:每次配置完,看一眼 .config 文件。里面全是 CONFIG_XXX=y 或 CONFIG_XXX=m 这样的行。比如:

CONFIG_I2C=y
CONFIG_I2C_CHARDEV=m
CONFIG_I2C_ALGOBIT=y

这表示 I2C 核心编译进内核,字符设备接口编译成模块,位操作算法编译进内核。

避坑指南: 我曾经在配置完 make menuconfig 后,直接 make,结果编译报错。查了半天,发现是 .config 里有冲突——两个驱动选了同一个IO地址。所以配置完最好 make olddefconfig 一下,让内核自动处理依赖和冲突。

3.4 知识结构总览

下面这张图,把内核源码结构、Kconfig/Makefile 机制、配置系统的关系串起来了。你看完应该能有个整体印象。

Linux内核配置与编译体系 内核源码目录树 arch/ drivers/ fs/ include/ Kconfig 配置定义 tristate / depends / select Makefile 编译规则 obj-y / obj-m / obj-n 读取 生成 .config 内核配置系统 make menuconfig .config 文件 make olddefconfig 编译输出:vmlinux / modules 💡 关键流程: Kconfig 定义配置项 → menuconfig 生成 .config → Makefile 根据 .config 编译源码 → 输出内核镜像和模块 做BSP移植时,你主要在 arch/ 和 drivers/ 下加Kconfig和Makefile,然后通过配置系统把它们「激活」。

3.5 实战要点:如何快速定位代码

最后分享几个我常用的方法:

  • 找驱动:先看 drivers/ 下按类别分的目录,比如 drivers/net/、drivers/i2c/。再进对应目录看 Kconfig,找到你想要的配置项名字。
  • 找板级文件:arch/arm/mach-xxx/ 或 arch/arm/boot/dts/(设备树时代)。我习惯先搜 Kconfig 里有没有 MACH_ 开头的选项。
  • 验证配置:改完 Kconfig 或 Makefile,执行 make menuconfig 看选项有没有出现。再 grep CONFIG_XXX .config 确认值对不对。
小技巧: 用 make help 可以列出所有配置目标。比如 make listnewconfig 能列出新增的配置项,适合内核版本升级后检查。

内核源码结构这东西,光看没用。你得动手。找个熟悉的板子,下载对应内核,进去翻一翻 arch/ 和 drivers/,再跑一遍 make menuconfig。走完一遍流程,比看十遍文档都管用。