3. 内核开发环境搭建:获取源码、配置编译与测试验证
搭建一个稳定、可复现的内核开发环境是驱动开发的第一步。本章将系统讲解如何获取内核源码、配置与编译内核、构建可加载模块,并利用 QEMU/KVM 建立高效的虚拟化测试环境。掌握这些基础操作,能够让你在后续章节中专注于 API 变更分析与适配,而不会被环境问题困扰。
3.1 获取内核源码
内核源码的获取方式主要有两种:Git 仓库克隆(适用于持续跟踪主线或长期支持(LTS)分支)和 tarball 下载(适用于固定版本、离线环境或快速验证)。
| 方式 | 命令/URL | 适用场景 | 磁盘占用 |
|---|---|---|---|
| Git 克隆(主线) | git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git |
跟踪最新开发、贡献补丁 | ~3.5 GB(含完整历史) |
| Git 克隆(LTS) | git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git |
长期维护、产品级开发 | ~2.8 GB(含稳定分支历史) |
| tarball 下载 | wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.6.tar.xz |
固定版本、离线环境、CI 构建 | ~1.2 GB(仅单版本源码) |
版本选择建议:对于驱动开发课程,推荐使用最新的 LTS 版本(如 v6.6.x)作为基线。LTS 版本 API 相对稳定,社区支持周期长,且大多数企业级产品基于 LTS 内核。若需适配较新内核 API,可同时克隆主线分支进行对比。
3.1.1 使用 Git 管理多个内核版本
驱动开发者经常需要在不同内核版本间切换,以验证 API 兼容性。推荐的工作区结构如下:
# 创建内核源码工作目录
mkdir -p ~/kernel-workspace && cd ~/kernel-workspace
# 克隆主线仓库(裸仓库,节省空间)
git clone --bare git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git linux-bare
# 基于裸仓库创建多个工作树
git worktree add ~/kernel-workspace/v6.6 v6.6
git worktree add ~/kernel-workspace/v6.7-rc1 v6.7-rc1
git worktree add ~/kernel-workspace/v6.5 v6.5
# 查看所有工作树
git worktree list
使用 git worktree 可以在同一台机器上同时检出多个版本,每个工作树独立编译,共享裸仓库的对象存储,大幅节省磁盘空间。
3.2 配置与编译内核
获取源码后,需要根据目标硬件或虚拟化平台配置内核选项,然后编译内核镜像和模块。以下是标准流程:
3.2.1 内核配置方法对比
| 配置命令 | 交互方式 | 推荐场景 |
|---|---|---|
make defconfig |
自动生成默认配置 | 快速开始、x86_64 通用平台 |
make menuconfig |
终端图形菜单(ncurses) | 手动调整选项、开发调试 |
make olddefconfig |
基于现有 .config 更新 | 升级内核版本时保留旧配置 |
make localmodconfig |
基于当前系统加载的模块 | 精简内核、减少编译时间 |
关键配置选项:驱动开发阶段,建议开启以下调试相关选项:
CONFIG_DEBUG_KERNEL=y — 内核调试支持CONFIG_DEBUG_INFO=y — 生成调试符号(增加编译时间但便于分析崩溃)CONFIG_KALLSYMS=y — 符号表支持CONFIG_DYNAMIC_DEBUG=y — 动态调试(pr_debug 控制)CONFIG_KASAN=y — 内核地址消毒器(检测内存错误,会降低性能)
3.2.2 编译内核与模块
# 进入内核源码目录
cd ~/kernel-workspace/v6.6
# 生成默认配置(x86_64)
make defconfig
# 手动调整配置(可选)
make menuconfig
# 编译内核镜像和模块(使用 -j 指定并行任务数,通常为 CPU 核心数 × 1.5)
make -j$(nproc) bzImage
make -j$(nproc) modules
# 安装模块到临时目录(避免污染系统)
make modules_install INSTALL_MOD_PATH=~/kernel-workspace/modules-install
# 查看编译产物
ls -lh arch/x86/boot/bzImage
ls -lh ~/kernel-workspace/modules-install/lib/modules/
注意:不要在开发主机上直接执行
make install 或 make modules_install(无 INSTALL_MOD_PATH),这会覆盖系统内核和模块,可能导致系统无法启动。始终使用 INSTALL_MOD_PATH 将模块安装到独立目录,或使用 QEMU 虚拟机进行测试。
3.3 构建可加载内核模块
驱动开发的核心产出是 可加载内核模块(LKM)。以下是一个最小模块的 Makefile 和构建流程:
3.3.1 最小模块示例
// hello.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init hello_init(void)
{
pr_info("Hello, Kernel Module loaded!\n");
return 0;
}
static void __exit hello_exit(void)
{
pr_info("Hello, Kernel Module unloaded!\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kernel Driver Developer");
MODULE_DESCRIPTION("A minimal kernel module example");
MODULE_VERSION("1.0");
# Makefile
obj-m := hello.o
# KDIR 指向已编译的内核源码目录
KDIR := /home/user/kernel-workspace/v6.6
all:
$(MAKE) -C $(KDIR) M=$(PWD) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) clean
# 构建模块
make
# 查看生成的模块
ls -lh hello.ko
# 使用 modinfo 查看模块信息
modinfo hello.ko
3.4 QEMU/KVM 测试环境搭建
在虚拟机中测试内核模块可以避免破坏宿主机,并且可以快速重启、快照回滚。QEMU 配合 KVM 加速是内核开发者的首选方案。
3.4.1 环境准备
# 安装 QEMU 及相关工具(Ubuntu/Debian)
sudo apt-get install qemu-system-x86 qemu-utils libvirt-daemon-system virt-manager
# 确认 KVM 可用
ls -l /dev/kvm
# 若不存在,检查 CPU 虚拟化是否开启:egrep -c '(vmx|svm)' /proc/cpuinfo
3.4.2 创建根文件系统镜像
内核需要根文件系统才能启动。使用 busybox 构建最小根文件系统:
# 下载并编译 busybox
wget https://busybox.net/downloads/busybox-1.36.1.tar.bz2
tar -xf busybox-1.36.1.tar.bz2 && cd busybox-1.36.1
make defconfig
make -j$(nproc)
make install
# 创建根文件系统目录结构
mkdir -p ~/kernel-workspace/rootfs/{bin,sbin,etc,proc,sys,dev,lib,usr,root,tmp}
# 复制 busybox 安装文件
cp -r _install/* ~/kernel-workspace/rootfs/
# 创建必要的设备节点
sudo mknod ~/kernel-workspace/rootfs/dev/console c 5 1
sudo mknod ~/kernel-workspace/rootfs/dev/null c 1 3
# 创建 init 脚本
cat > ~/kernel-workspace/rootfs/init << 'EOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
echo "Welcome to Kernel Dev Environment"
exec /bin/sh
EOF
chmod +x ~/kernel-workspace/rootfs/init
# 打包为 cpio 归档
cd ~/kernel-workspace/rootfs
find . | cpio -o --format=newc > ../rootfs.cpio
gzip -f ../rootfs.cpio
3.4.3 启动 QEMU 虚拟机并加载模块
# 启动 QEMU(使用内核镜像和根文件系统)
qemu-system-x86_64 \
-kernel ~/kernel-workspace/v6.6/arch/x86/boot/bzImage \
-initrd ~/kernel-workspace/rootfs.cpio.gz \
-append "console=ttyS0 root=/dev/ram rdinit=/init" \
-nographic \
-m 512M \
-smp 2 \
-enable-kvm \
-netdev user,id=net0 \
-device e1000,netdev=net0
# 在 QEMU 控制台中加载模块(需先将 hello.ko 放入根文件系统)
# insmod /hello.ko
# lsmod
# rmmod hello
# dmesg | tail
调试技巧:在 QEMU 启动参数中添加
-s 会在 TCP 1234 端口开启 GDB 服务器,宿主机可使用 gdb vmlinux 并执行 target remote :1234 进行内核源码级调试。这是定位驱动崩溃和 API 使用错误的利器。
3.4.4 使用共享目录加速模块迭代
每次修改模块后重新打包根文件系统效率较低。推荐使用 QEMU 的 virtio-9p 共享目录:
# 宿主机启动 QEMU 时添加共享目录
qemu-system-x86_64 \
-kernel ~/kernel-workspace/v6.6/arch/x86/boot/bzImage \
-initrd ~/kernel-workspace/rootfs.cpio.gz \
-append "console=ttyS0 root=/dev/ram rdinit=/init" \
-nographic -m 512M -enable-kvm \
-fsdev local,security_model=passthrough,id=fsdev0,path=~/kernel-workspace/modules \
-device virtio-9p-pci,fsdev=fsdev0,mount_tag=modshare
# 在 QEMU 虚拟机内挂载共享目录
mkdir /mnt/modules
mount -t 9p -o trans=virtio,version=9p2000.L modshare /mnt/modules
cd /mnt/modules
insmod hello.ko
3.5 环境验证清单
完成环境搭建后,建议执行以下验证步骤,确保所有组件正常工作:
| 步骤 | 验证命令 | 预期结果 |
|---|---|---|
| 1. 内核编译 | make -j$(nproc) bzImage |
生成 arch/x86/boot/bzImage,无错误 |
| 2. 模块编译 | make -C $(KDIR) M=$(PWD) modules |
生成 .ko 文件,无警告 |
| 3. 模块信息 | modinfo hello.ko |
显示作者、描述、许可证等信息 |
| 4. QEMU 启动 | 执行 QEMU 启动命令 | 虚拟机进入 shell 提示符 |
| 5. 模块加载 | 在虚拟机内 insmod hello.ko |
dmesg 显示加载消息 |
| 6. 模块卸载 | 在虚拟机内 rmmod hello |
dmesg 显示卸载消息 |
| 7. 共享目录 | 挂载 9p 文件系统 | 可读写宿主机模块目录 |
最佳实践总结:
• 使用
• 始终使用
• 利用 QEMU + KVM + 9p 共享目录实现“宿主机编译 → 虚拟机加载”的快速迭代循环。
• 开启内核调试选项(DEBUG_KERNEL、DEBUG_INFO、KASAN)以便快速定位问题。
• 将环境搭建步骤写成脚本(如
• 使用
git worktree 管理多个内核版本,避免重复克隆。• 始终使用
INSTALL_MOD_PATH 将模块安装到独立目录,不污染宿主机。• 利用 QEMU + KVM + 9p 共享目录实现“宿主机编译 → 虚拟机加载”的快速迭代循环。
• 开启内核调试选项(DEBUG_KERNEL、DEBUG_INFO、KASAN)以便快速定位问题。
• 将环境搭建步骤写成脚本(如
setup-env.sh),方便团队复现和 CI 集成。