4. Linux内核源码结构与配置系统
说实话,我刚接触内核源码那会儿,第一反应就是——这目录也太多了吧?几十个文件夹,上千个文件,根本不知道从哪下手。后来做项目多了,慢慢摸清了门道。今天我就带你把这套结构拆开看看,再聊聊Kconfig和Makefile是怎么配合的,最后说说那几个配置工具怎么用顺手。
4.1 内核源码目录树解析
先看顶层目录。你下载完内核源码,ls一下,大概能看到这些:
arch/ block/ crypto/ Documentation/
drivers/ fs/ include/ init/
ipc/ kernel/ lib/ mm/
net/ samples/ scripts/ security/
sound/ tools/ usr/ virt/
每个目录都有明确分工。我列个表,你一看就明白:
| 目录 | 作用 | 我常关注的点 |
|---|---|---|
| arch/ | 体系结构相关代码 | ARM、x86、RISC-V都在这里,裁剪移植主要动它 |
| drivers/ | 设备驱动 | 占源码一半以上,裁剪时重点砍这里 |
| fs/ | 文件系统实现 | ext4、proc、sysfs等,嵌入式常用只读文件系统 |
| kernel/ | 核心调度、进程管理 | 一般不动,除非做实时优化 |
| mm/ | 内存管理 | 小内存设备要关注SLAB/SLUB配置 |
| net/ | 网络协议栈 | 物联网设备可以砍掉IPv6等不用的协议 |
| include/ | 头文件 | ARM相关在include/linux/和arch/arm/include/ |
我个人习惯,拿到一个新板子,先看arch/arm/下面的mach-*和plat-*目录。这些是板级支持包,里面定义了内存映射、时钟、中断这些底层东西。嗯,这里要注意——新版本内核已经逐步用设备树(DT)替代了板级文件,但老项目里还是经常见到。
arch/arm/configs/目录。里面有很多现成的默认配置,比如vexpress_defconfig、imx_v7_defconfig。拿一个接近你硬件的配置做起点,比从零开始省事太多。
4.2 Kconfig与Makefile机制
这两个文件是内核配置系统的核心。说白了,Kconfig负责“有什么选项可以选”,Makefile负责“选了之后怎么编译”。
4.2.1 Kconfig——配置选项的定义
每个子目录下基本都有Kconfig文件。举个例子,drivers/i2c/Kconfig里可能有这样的内容:
config I2C_CHARDEV
tristate "I2C character device"
depends on I2C
help
Say Y here to enable I2C character device interface.
This is needed for i2c-dev userspace access.
这里tristate表示三种状态:不编译、编译进内核(Y)、编译成模块(M)。depends on是依赖关系——如果I2C没选,这个选项根本不会出现。
我曾经在一个项目里踩过坑:选了一个驱动,编译报错说某个函数未定义。查了半天,发现是依赖的另一个模块没选。从那以后,我每次改配置都会用make menuconfig的搜索功能(按/键),看看依赖链是否完整。
4.2.2 Makefile——编译的执行者
Kconfig定义完选项,Makefile根据配置结果决定编译哪些文件。还是拿I2C举例:
obj-$(CONFIG_I2C_CHARDEV) += i2c-dev.o
如果CONFIG_I2C_CHARDEV被设为y,就编译进内核;设为m,就编译成模块;设为n,就不编译。
你想想看,整个内核的编译系统就是这么一层层拼起来的。顶层Makefile递归进入各个子目录,每个子目录的Makefile根据Kconfig生成的.config文件决定编译什么。
- Kconfig定义选项 → 生成菜单界面
- 用户选择 → 生成
.config文件 - Makefile读取
.config→ 决定编译对象 - 编译链接 → 生成内核镜像和模块
4.3 内核配置工具的使用
配置工具有好几种,我逐个说说。
4.3.1 menuconfig——最常用的文本界面
这是我最常用的工具。在源码根目录执行:
make menuconfig
界面是字符终端里的菜单,用方向键导航,空格键切换选择。几个快捷键要记住:
/:搜索选项,输入关键字就能找到位置?:查看选项的帮助信息Z:显示所有选项(包括被依赖隐藏的)Esc+Esc:返回上一级
我个人习惯,每次改完配置都会按Shift+/查看帮助,确认依赖关系没问题。特别是depends on和select这两个关键字,搞反了会导致配置不一致。
CONFIG_USB_SUPPORT却没选CONFIG_USB,结果编译出来的内核根本没有USB功能。后来养成习惯:改完配置先make savedefconfig生成精简配置,再检查一遍。
4.3.2 xconfig——图形化配置
如果你有图形桌面环境,可以用:
make xconfig
这个工具基于Qt,界面更直观。左侧是树形菜单,右侧是选项详情。我个人觉得xconfig在查看依赖关系时比menuconfig方便——它会把被依赖的选项高亮显示。
不过说实话,我大部分时间还是用menuconfig。为什么?因为嵌入式开发经常在服务器上做,没有图形界面。menuconfig在任何终端都能跑,而且用熟了效率很高。
4.3.3 其他配置工具
| 工具 | 命令 | 适用场景 |
|---|---|---|
| nconfig | make nconfig | 基于ncurses,比menuconfig操作更流畅 |
| gconfig | make gconfig | 基于GTK+,和xconfig类似 |
| oldconfig | make oldconfig | 基于已有.config更新,只问新增选项 |
| defconfig | make defconfig | 生成架构默认配置 |
嗯,这里要提一下oldconfig。我每次升级内核版本,都会先用旧版本的.config跑一遍make oldconfig。它会自动保留旧配置,只让你确认新版本新增的选项。这样升级起来平滑很多。
4.4 内核配置系统核心逻辑图
下面这张图展示了Kconfig、Makefile、.config三者之间的关系,以及配置工具在整个流程中的位置:
4.5 实战建议
说了这么多,最后给几条实在的建议:
- 从默认配置开始:别自己从头配。用
make <board>_defconfig生成基础配置,再按需裁剪。 - 善用搜索:menuconfig里按
/,输入关键字就能找到选项位置。我配驱动时经常用这个功能。 - 保存精简配置:配完后执行
make savedefconfig,生成只包含非默认选项的精简配置,方便版本管理。 - 对比配置差异:用
diff比较两个.config文件,能快速看出改了哪些选项。
modprobe加载试试,不行就卸掉,不影响内核启动。等确定需要了,再改成编译进内核(Y)。
内核配置系统说白了就是一套“选择题+编译规则”的组合。Kconfig出题,你作答,Makefile根据答案干活。搞懂这个逻辑,后面裁剪移植就顺了。