在 Linux 上编译和调试内核的完整指南
第一阶段:环境准备
1. 安装必需的编译工具和依赖
在 Ubuntu/Debian 系统上:
1 | sudo apt update |
在 CentOS/RHEL/Fedora 系统上,使用 yum 或 dnf 安装类似的包。
2. 获取内核源码
方法A:从官方仓库克隆(推荐,获取最新版本)
1 | git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git |
方法B:下载稳定版本压缩包
从 https://kernel.org 下载一个稳定的源码压缩包(如 linux-6.6.6.tar.xz),然后解压:
1 | tar -xvf linux-6.6.6.tar.xz |
第二阶段:配置与编译
3. 内核配置
这是最关键的一步。你可以基于当前运行的内核配置来开始,这样可以减少不必要的麻烦。
1 | # 将当前系统的配置复制到源码目录 |
运行配置菜单:
1 | make menuconfig |
在出现的图形化界面中,确保开启以下关键调试选项(使用空格键选择,* 表示编译进内核,M 表示编译为模块):
- Kernel hacking ->
- Compile-time checks and compiler options ->
- [*] Compile the kernel with debug info (CONFIG_DEBUG_INFO) - 这是最重要的,生成调试符号。
- [*] Generate dwarf4 debuginfo (CONFIG_DEBUG_INFO_DWARF4)
- [*] Provide GDB scripts for kernel debugging (CONFIG_GDB_SCRIPTS)
- [*] KGDB: kernel debugger (CONFIG_KGDB)
保存并退出配置菜单。
如果直接使用现有配置,可以运行 make olddefconfig 来无声地接受所有新选项的默认值。
- Compile-time checks and compiler options ->
4. 编译内核
使用 -j 参数来并行编译,数字为你的CPU核心数 * 2,以大幅加快速度(例如,8核CPU用 -j16)。
1 | # 获取CPU核心数 |
这个过程会持续很长时间(从几十分钟到几小时),取决于你的硬件性能。
编译完成后,主要生成以下文件:
- vmlinux: 在源码根目录,这是带有调试信息的原始内核可执行文件,是GDB调试的主要对象。
- arch/x86/boot/bzImage: 压缩后的可引导内核镜像,用于启动系统。
第三阶段:使用 QEMU 启动和调试
为了避免损坏你的主机系统,在虚拟机中调试是最安全的方式。
5. 准备一个最小的根文件系统(rootfs)
我们需要一个简单的环境来运行内核。BusyBox 是一个很好的选择。
- 下载并编译 BusyBox:
1 | cd .. |
在 BusyBox 设置中,进入 Settings -> [*] Build static binary (no shared libs)。
1 | make -j$(nproc) && make install |
- 创建根文件系统:
1 | cd _install |
- 创建一个简单的 init 脚本(必须是可执行的):
1 | cat > init << EOF |
- 打包成 initramfs:
1 | find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../initramfs.cpio.gz |
6. 使用 QEMU 启动编译好的内核
1 | qemu-system-x86_64 \ |
参数解释:
- -s: 缩写,相当于 -gdb tcp::1234,在TCP端口1234上开启GDB调试服务。
- -S: 在启动时冻结CPU,等待GDB发出 continue 指令后再继续。这是开始调试的关键。
- -nographic: 无图形界面,将QEMU控制台输出到当前终端。
- -append “nokaslr”: 禁用内核地址空间布局随机化,这是调试所必需的,否则断点会错位。
- -smp and -m: 指定CPU核心数和内存大小。
此时,QEMU窗口会黑屏并等待GDB连接。
7. 使用 GDB 连接并调试
打开另一个终端标签页,进入你的内核源码目录。
- 启动 GDB,并加载带有调试信息的 vmlinux 文件:
1 | cd linux |
- 在 GDB 中连接目标:
1 | (gdb) break start_kernel |
- target remote :1234:连接到正在等待的 QEMU 实例。
- break start_kernel:在内核启动的早期阶段设置一个断点。
- continue:让被 -S 冻结的虚拟机继续运行,它很快就会在 start_kernel 处停下。
开始调试:
现在你可以像调试普通程序一样使用 GDB 命令了:
- next (n): 单步跳过
- step (s): 单步进入
- break
: 设置断点 - list: 查看源代码
- print
: 打印变量值 - continue (c): 继续运行
- backtrace (bt): 查看调用栈
clangd
为 Linux 内核生成 compile_commands.json:
1 | make compile_commands.json |
生成 Clang 兼容的编译数据库:
使用 scripts/gen_compile_commands.py(内核自带脚本)生成时,它会自动过滤掉 Clang 不支持的选项:
1 | make CC=clang compile_commands.json |