3、开发环境搭建:内核源码树准备、编译工具链配置、内核编译选项、QEMU/KVM虚拟机环境搭建
说实话,做内核热补丁开发,环境搭建这一步最容易被忽视。很多人一上来就急着写补丁,结果编译不过、加载失败,折腾半天才发现是工具链版本不对,或者内核选项没开。我当年就吃过这个亏,白白浪费了两天时间。
这一节,咱们把环境搭得稳稳当当的。你跟着我的步骤来,保证后面开发顺风顺水。
3.1 内核源码树准备
首先得有一份内核源码。我个人习惯用主线内核,因为文档全、社区支持好。但如果你在生产环境做热补丁,建议用发行版自带的内核源码,比如 CentOS 的 kernel-devel 包。
这里我推荐两种方式:
- 方式一:直接下载主线内核
去 kernel.org 下载最新稳定版。我一般用 5.10 LTS,生命周期长,适合做产品。 - 方式二:用发行版源码包
Ubuntu 下执行apt-get source linux-image-$(uname -r),CentOS 下用yumdownloader --source kernel。
下载完后,解压到 /usr/src/ 目录。我个人习惯建个软链接:
ln -s /usr/src/linux-5.10.210 /lib/modules/$(uname -r)/build
这样后面编译模块时,系统能自动找到源码路径。
git clone 拉取 Linus 的树,然后 git checkout 到目标版本。这样切换分支非常快,而且能保留你的修改记录。
3.2 编译工具链配置
工具链这块,说白了就是 gcc、make、elfutils 这几样。但版本有讲究——太新的 gcc 可能编译老内核报错,太旧的又缺特性。
我建议的版本组合:
| 工具 | 推荐版本 | 备注 |
|---|---|---|
| gcc | 9.x ~ 11.x | 太新(如13.x)可能遇到内联汇编兼容问题 |
| make | 4.3+ | 低版本 make 解析内核 Kbuild 会卡住 |
| elfutils | 0.186+ | 热补丁需要 libelf 解析符号表 |
| openssl-devel | 1.1.1+ | 内核模块签名需要 |
安装命令很简单:
# Ubuntu/Debian
sudo apt-get install build-essential libelf-dev libssl-dev
# CentOS/RHEL
sudo yum groupinstall "Development Tools"
sudo yum install elfutils-libelf-devel openssl-devel
嗯,这里要注意:如果你用 CentOS 7,默认 gcc 是 4.8.5,太老了。我曾经在这个坑里爬了半天——编译出来的内核模块加载时直接 panic。解决办法是用 devtoolset 升级 gcc:
sudo yum install centos-release-scl
sudo yum install devtoolset-9-gcc*
scl enable devtoolset-9 bash
3.3 内核编译选项配置
热补丁依赖三个核心内核选项:CONFIG_FTRACE、CONFIG_KPROBES、CONFIG_LIVEPATCH。缺一个,你的补丁就加载不了。
进入内核源码目录,执行:
make menuconfig
然后按下面的路径打开选项:
- CONFIG_FTRACE:
Kernel hacking → Tracers → Enable Ftrace(必选)
子选项里建议把FUNCTION_TRACER和FUNCTION_GRAPH_TRACER也打开,调试时很有用。 - CONFIG_KPROBES:
General setup → Kprobes(必选)
同时打开OPTPROBES,能提升性能。 - CONFIG_LIVEPATCH:
General setup → Live Patching(必选)
这个选项在 4.0+ 内核才有,老内核就别想了。
.config 文件,确保以下三行存在:
CONFIG_FTRACE=y
CONFIG_KPROBES=y
CONFIG_LIVEPATCH=y
然后执行 make olddefconfig 自动补全依赖项。
配置完后,编译内核:
make -j$(nproc) bzImage
make -j$(nproc) modules
sudo make modules_install
sudo make install
编译时间取决于你的机器。我笔记本 8 核,大概 15 分钟。第一次编译建议别开 -j 太大,容易 OOM。
linux-image-generic),这些选项默认就是打开的。但定制内核时很容易漏掉。我曾经有一次忘了开 CONFIG_LIVEPATCH,结果补丁编译通过但加载时报 ENOSYS,查了半天才发现是内核不支持。
3.4 QEMU/KVM 虚拟机环境搭建
说实话,我不建议在物理机上直接调试热补丁。万一补丁写崩了,系统直接挂掉,你连抢救的机会都没有。用虚拟机最安全,快照一恢复,又是一条好汉。
我推荐用 QEMU/KVM 方案,性能接近原生,而且支持嵌套虚拟化——你可以在虚拟机里再跑一个虚拟机来测试。
安装步骤:
# Ubuntu
sudo apt-get install qemu-kvm libvirt-daemon-system libvirt-clients bridge-utils virt-manager
# CentOS
sudo yum install qemu-kvm libvirt libvirt-python libguestfs-tools virt-install
启动 libvirtd 服务:
sudo systemctl enable libvirtd
sudo systemctl start libvirtd
然后创建一台虚拟机。我习惯用 virt-install 命令行,比图形界面快:
virt-install \
--name kpatch-dev \
--ram 4096 \
--vcpus 4 \
--disk path=/var/lib/libvirt/images/kpatch-dev.qcow2,size=40 \
--os-variant centos7.0 \
--network bridge=virbr0 \
--location /path/to/CentOS-7-x86_64-Minimal-2009.iso
装好系统后,把刚才编译好的内核拷贝进去:
scp /boot/vmlinuz-5.10.210-custom root@vm-ip:/boot/
scp /boot/System.map-5.10.210-custom root@vm-ip:/boot/
然后在虚拟机里更新 grub,重启就能用新内核了。
virsh snapshot-revert,10 秒恢复。比重新装系统快多了。
3.5 验证环境是否就绪
环境搭好了,得验证一下。写个简单的内核模块,看看能不能正常编译和加载:
// test_module.c
#include <linux/module.h>
#include <linux/kernel.h>
static int __init test_init(void)
{
printk(KERN_INFO "Hello from kpatch dev env!\n");
return 0;
}
static void __exit test_exit(void)
{
printk(KERN_INFO "Goodbye!\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
Makefile:
obj-m := test_module.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
编译后加载:
sudo insmod test_module.ko
sudo rmmod test_module.ko
dmesg | tail -2
如果看到 "Hello" 和 "Goodbye",说明环境没问题了。
最后,检查内核选项是否真的生效:
cat /boot/config-$(uname -r) | grep -E "CONFIG_FTRACE|CONFIG_KPROBES|CONFIG_LIVEPATCH"
三个选项都应该是 =y 或 =m。如果哪个是 is not set,回去重新配置吧。
环境搭建就到这里。说白了,这一步花点时间把基础打牢,后面写补丁、调试才能顺风顺水。我见过太多人跳过这一步直接写代码,最后在环境问题上浪费的时间比写代码还多。
公众号:蓝海资料掘金营,微信deep3321