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 里配置后生成的。
3.3 内核配置系统详解:从 menuconfig 到 .config
配置系统说白了就是三个环节:
- 配置界面:menuconfig、xconfig、nconfig 等
- 配置数据库:.config 文件
- 配置解析器: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 机制、配置系统的关系串起来了。你看完应该能有个整体印象。
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。走完一遍流程,比看十遍文档都管用。