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 可以在同一台机器上同时检出多个版本,每个工作树独立编译,共享裸仓库的对象存储,大幅节省磁盘空间。

Git Worktree 多版本管理结构 裸仓库 (linux-bare) 工作树 v6.6 工作树 v6.7-rc1 工作树 v6.5 优势 共享对象存储,节省磁盘;独立工作目录,并行编译;快速切换版本

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
内核模块构建与加载流程 源码 (hello.c) Makefile 内核源码树 (KDIR) make 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
QEMU/KVM 内核开发测试环境架构 宿主机 (Host) 内核源码树 模块构建目录 QEMU 进程 GDB 调试器 虚拟机 (Guest) 测试内核 (bzImage) 根文件系统 加载的模块 virtio-9p 共享 KVM 加速 9p 共享目录 GDB 远程调试 (:1234)

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 文件系统 可读写宿主机模块目录
最佳实践总结:
• 使用 git worktree 管理多个内核版本,避免重复克隆。
• 始终使用 INSTALL_MOD_PATH 将模块安装到独立目录,不污染宿主机。
• 利用 QEMU + KVM + 9p 共享目录实现“宿主机编译 → 虚拟机加载”的快速迭代循环。
• 开启内核调试选项(DEBUG_KERNEL、DEBUG_INFO、KASAN)以便快速定位问题。
• 将环境搭建步骤写成脚本(如 setup-env.sh),方便团队复现和 CI 集成。