3. 内核源码获取与配置:从 kernel.org 下载源码、使用 git 管理内核树、内核配置系统(Kconfig/Makefile)
做内核开发,第一步就是拿到源码。这事听起来简单,但里面坑不少。我见过不少新手直接从网盘下个压缩包就开始搞,结果编译到一半发现版本不对,或者 git 管理混乱,改了什么自己都记不清。今天咱们就把这第一步走扎实。
3.1 从 kernel.org 获取源码
官方源头只有一个:kernel.org。别去其他地方下,容易被植入后门或者拿到被修改过的代码。
我个人习惯用 curl 或者 wget 直接拉取主线内核。比如要获取 6.1 LTS 版本:
# 下载完整源码包
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz
# 验证签名(强烈建议做)
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.sign
xz -cd linux-6.1.tar.xz | gpg --verify linux-6.1.tar.sign -
内核源码包主要有三种格式:
| 格式 | 压缩率 | 解压速度 | 适用场景 |
|---|---|---|---|
.tar.gz |
中等 | 快 | 快速下载、临时使用 |
.tar.xz |
高(体积小 30-40%) | 较慢 | 长期保存、带宽有限 |
.tar.bz2 |
较高 | 中等 | 兼容旧系统 |
我建议用 .tar.xz,体积最小,省带宽。解压命令:
tar -xf linux-6.1.tar.xz
cd linux-6.1
3.2 使用 Git 管理内核树
下载压缩包只是临时方案。真正做开发,必须用 git。为什么?因为内核社区每天都在提交新代码,你拿到的压缩包在下载那一刻就已经过时了。
克隆主线仓库:
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
这一下会拉取整个历史,大概 3-5GB。如果你只想看最新代码,可以加 --depth=1:
git clone --depth=1 git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
git fetch --unshallow 补全。这样既快又灵活。
内核的 git 分支管理很有讲究。主线(master/main)是 Linus 维护的,每天都有新提交。稳定分支(如 linux-6.1.y)由 Greg Kroah-Hartman 维护,只修 bug 不加功能。我建议初学者用 稳定分支,问题少,文档全。
# 查看所有远程分支
git branch -r
# 切换到 6.1 稳定分支
git checkout --track origin/linux-6.1.y
为什么要用 git?我给你列几个实际好处:
- 回滚方便:改坏了直接
git checkout -- .恢复 - 打补丁容易:
git format-patch生成标准补丁 - 追踪修改:
git blame看每行代码是谁改的 - 管理多个版本:一个仓库可以同时维护 5.10、6.1、6.6 三个分支
我在做嵌入式项目时,经常需要同时维护多个内核版本。用 git 分支管理,每个客户一个分支,改了什么一目了然。要是用压缩包,早就乱成一锅粥了。
3.3 内核配置系统:Kconfig 与 Makefile
拿到源码后,下一步就是配置。内核的配置系统非常庞大,但核心只有两个东西:Kconfig 和 Makefile。
先看一张整体流程图:
3.3.1 Kconfig:配置选项的定义
Kconfig 文件分散在内核各个子目录中。每个驱动、每个功能模块都有自己的 Kconfig。它的语法其实不复杂,核心就是 config、menuconfig、choice 这几个关键字。
举个例子,看一个简单的 Kconfig 片段:
config SAMPLE_DRIVER
tristate "Sample driver support"
depends on PCI
select MISC_DEVICES
help
This is a sample driver for demonstration.
Say Y here to compile it into the kernel.
Say M to build it as a module.
这里的关键点:
- tristate:三态选项(Y=编译进内核,M=编译为模块,N=不编译)
- depends on:依赖关系,比如这个驱动依赖 PCI 子系统
- select:反向依赖,选中它会自动选上 MISC_DEVICES
depends on NETDEVICES,结果用户在配置时能看到选项,但编译永远报错。排查了半天才发现是 Kconfig 没写对。
3.3.2 Makefile:条件编译的执行
Kconfig 定义完选项,Makefile 负责执行。内核的 Makefile 体系叫 Kbuild,和普通的 Makefile 不太一样。
看一个典型的驱动 Makefile:
obj-$(CONFIG_SAMPLE_DRIVER) += sample_driver.o
sample_driver-objs := main.o ioctl.o probe.o
这里 obj-$(CONFIG_SAMPLE_DRIVER) 是关键。如果 CONFIG_SAMPLE_DRIVER=y,就变成 obj-y,编译进内核。如果是 m,就变成 obj-m,编译成模块。如果是空,就不编译。
常用的 Makefile 变量:
| 变量 | 含义 | 示例 |
|---|---|---|
obj-y |
编译进内核 | obj-y += usb/ |
obj-m |
编译为模块 | obj-m += my_driver.o |
obj-n |
不编译 | 默认值 |
ccflags-y |
附加编译选项 | ccflags-y += -DDEBUG |
subdir-ccflags-y |
子目录编译选项 | subdir-ccflags-y += -Wall |
3.3.3 配置方法实战
实际配置内核,我推荐用 make menuconfig。它基于 ncurses,在终端里就能操作,比图形界面快得多。
# 先加载默认配置(以 x86 为例)
make defconfig
# 然后手动调整
make menuconfig
进入 menuconfig 后,你会看到树形结构。按 / 可以搜索选项,比如搜 SAMPLE_DRIVER。按 Y 编译进内核,按 M 编译为模块,按 N 排除。
make defconfig 生成一个基础配置,再用 make localmodconfig 精简。这个命令会分析当前系统加载的模块,只保留需要的驱动,能大幅减少编译时间。我在笔记本上编译内核,从 40 分钟降到了 8 分钟。
配置完成后,生成 .config 文件。你可以直接编辑它,但不建议手动改,容易搞乱依赖关系。正确的做法是:
# 保存当前配置
cp .config my_custom_config
# 下次用这个配置
cp my_custom_config .config
make olddefconfig # 自动处理新选项的默认值
make olddefconfig 这个命令很实用。当你升级内核版本后,新内核可能新增了一些配置选项。这个命令会用默认值填充新选项,保证你的配置能继续用。
3.4 避坑指南
最后,分享几个我踩过的坑:
- 不要用 root 编译:用普通用户编译,出问题不会搞坏系统。我见过有人用 root 编译,一个 Makefile 错误把
/lib/modules清空了。 - 保存 .config 到版本控制:把
.config提交到 git,方便回溯。我每个项目都会在仓库里保留一份config_xxx文件。 - 交叉编译时注意 ARCH:给 ARM 板子编译,要设置
ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-。忘了设 ARCH 的话,编译出来的还是 x86 的内核。 - 模块签名:如果开启了
CONFIG_MODULE_SIG,编译出来的模块只能加载到同一台机器上。换台机器就报Required key not available。调试阶段建议关掉这个选项。
嗯,源码获取和配置这部分就讲到这里。这些基础打牢了,后面编译和调试才能顺风顺水。