2. 开发环境搭建:选择合适的Linux发行版、安装内核头文件、配置编译器、使用QEMU搭建测试虚拟机

说实话,内核模块开发的门槛并不高。但很多人一开始就栽在了环境搭建上。我见过不少朋友,兴致勃勃地写了第一行代码,结果编译都过不了——不是头文件找不到,就是编译器版本不对。嗯,这章我们就来把这摊事理顺。

2.1 选择合适的Linux发行版

先聊聊发行版的选择。我个人习惯用 Ubuntu LTSDebian 做开发。为什么?因为社区活跃,遇到问题搜一下就有答案。你想想看,搞内核开发本来就够烧脑了,没必要在环境上再给自己添堵。

如果你问我推荐哪个版本:

  • Ubuntu 22.04 LTS24.04 LTS —— 稳定,包管理方便,内核头文件直接 apt 就能装
  • Debian 12 —— 更纯粹,适合喜欢干净系统的朋友
  • Fedora —— 内核更新快,但有时候太新反而容易踩坑
我的建议: 如果你是新手,直接用 Ubuntu 22.04 LTS。我在项目中遇到过用 Arch Linux 的同事,每次升级内核都要重新编译模块,折腾得够呛。稳定压倒一切。

2.2 安装内核头文件

内核模块开发,说白了就是跟内核 API 打交道。而这些 API 的声明都在内核头文件里。没有头文件,你连 printk 都用不了。

安装方法很简单:

# 先看看当前内核版本
uname -r

# 安装对应版本的头文件
sudo apt update
sudo apt install linux-headers-$(uname -r)

# 或者直接装所有头文件(省事)
sudo apt install linux-headers-generic

装完之后,头文件会放在 /usr/src/linux-headers-$(uname -r)/ 目录下。你可以进去看看,里面有个 include/linux 文件夹,我们常用的 module.hkernel.h 都在里面。

注意: 内核版本必须和头文件版本严格一致。我曾经因为升级了内核但没更新头文件,编译时报了一堆莫名其妙的错误,排查了半天才发现是版本不匹配。

2.3 配置编译器

内核模块的编译器和普通应用程序不太一样。我们用的是 交叉编译工具链 吗?不,大多数情况下直接用本机的 gcc 就行。但有几个关键点要注意:

  • gcc 版本不能太老 —— 建议 8.0 以上
  • 必须安装 make —— 内核模块用 Makefile 来编译
  • 需要 build-essential 包 —— 它包含了编译所需的基本工具
sudo apt install build-essential
gcc --version
make --version

这里有个小坑:有些发行版默认的 gcc 版本可能比较旧。你可以用 update-alternatives 来切换版本。我个人习惯装两个版本,一个稳定版,一个最新版,根据项目需求切换。

核心要点: 内核模块的编译不是直接调用 gcc,而是通过内核的构建系统(Kbuild)来完成的。我们只需要写一个简单的 Makefile,剩下的交给内核自己处理。

2.4 使用QEMU搭建测试虚拟机

为什么要用虚拟机?因为内核模块一旦写崩了,轻则 kernel panic,重则把整个系统搞挂。在物理机上调试?风险太大。我刚开始学的时候就在自己的笔记本上直接 insmod,结果系统直接死机,连保存文档的机会都没有。

QEMU 是个好东西。它轻量、快速,而且支持 GDB 调试。下面是我常用的搭建步骤:

2.4.1 安装 QEMU

sudo apt install qemu-system-x86 qemu-utils

2.4.2 准备内核和根文件系统

你可以用发行版自带的内核,也可以自己编译。我建议初学者先用现成的:

# 下载一个最小的根文件系统(比如 Alpine Linux)
wget https://dl-cdn.alpinelinux.org/alpine/v3.19/releases/x86_64/alpine-virt-3.19.0-x86_64.iso

# 或者用 busybox 自己做一个(更轻量)
# 这个我们后面章节会详细讲

2.4.3 启动虚拟机

qemu-system-x86_64 \
  -kernel /boot/vmlinuz-$(uname -r) \
  -initrd /boot/initrd.img-$(uname -r) \
  -hda alpine-virt-3.19.0-x86_64.iso \
  -append "console=ttyS0" \
  -nographic

启动之后,你就有了一个干净的内核环境。把编译好的 .ko 文件传进去,用 insmod 加载测试。就算崩了,重启虚拟机也就几秒钟的事。

避坑指南: 我曾经在虚拟机上测试模块,结果忘了关 SELinux,模块加载失败还查不出原因。建议在测试虚拟机里先关掉 SELinux 或 AppArmor,减少干扰因素。

2.5 本章知识体系

下面这张图帮你理清整个环境搭建的逻辑:

内核模块开发环境搭建 发行版选择 内核头文件 编译器配置 QEMU虚拟机 Ubuntu LTS / Debian Fedora / Arch linux-headers-$(uname -r) /usr/src/linux-headers-* gcc >= 8.0 build-essential qemu-system-x86 内核 + 根文件系统 核心流程 选择发行版 → 安装内核头文件 → 配置编译器 → 搭建QEMU测试环境 编写模块代码 → 编译(.ko) → 在QEMU中加载测试 → 调试优化

2.6 验证环境是否就绪

最后,写一个最简单的模块来验证环境:

// hello.c
#include <linux/module.h>
#include <linux/kernel.h>

static int __init hello_init(void) {
    printk(KERN_INFO "Hello, Kernel Module!\n");
    return 0;
}

static void __exit hello_exit(void) {
    printk(KERN_INFO "Goodbye, Kernel Module!\n");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple test module");
# Makefile
obj-m += hello.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

编译:

make

如果生成了 hello.ko 文件,恭喜你,环境搭建成功了!

总结一下: 环境搭建其实就四步——选系统、装头文件、配编译器、搭虚拟机。每一步都不难,但顺序不能乱。我见过有人先搭虚拟机再装头文件,结果发现虚拟机里没有编译器,又得重来。按部就班,稳扎稳打,后面写代码才能顺风顺水。

公众号:蓝海资料掘金营,微信deep3321