Linux内核驱动实战60例系统讲解 - 第1节
1.1 开发环境全景概览
Linux 内核驱动开发的第一步是搭建一个稳定、可复现的编译与运行环境。本章将引导你从零开始,完成虚拟机安装、交叉编译工具链配置、内核源码下载与编译,并最终编写并加载第一个内核模块——Hello World。
整个环境搭建流程可概括为以下四个阶段:
- 宿主机 + 虚拟机:在 Windows/macOS 上通过 VMware 或 VirtualBox 安装 Ubuntu 22.04 LTS。
- 交叉编译工具链:安装 arm-linux-gnueabihf- 系列工具,用于在 x86 主机上编译 ARM 架构内核与模块。
- 内核源码:从 kernel.org 下载稳定版 Linux 内核,配置并编译出内核镜像与模块。
- 第一个模块:编写一个简单的内核模块,使用 Makefile 编译,并在目标系统上加载/卸载。
💡 提示: 本课程所有实验均基于 Linux 6.1 LTS 与 ARM Cortex-A7 平台。如果你使用 x86 虚拟机直接运行,可跳过交叉编译步骤,直接使用本地 gcc 编译。
1.2 虚拟机安装与配置
1.2.1 选择虚拟化方案
| 方案 | 适用场景 | 性能 | 推荐指数 |
|---|---|---|---|
| VMware Workstation Pro | Windows 桌面用户 | ★★★★★ | ⭐⭐⭐⭐⭐ |
| VirtualBox | 跨平台、开源 | ★★★★☆ | ⭐⭐⭐⭐ |
| QEMU + libvirt | Linux 原生、ARM 模拟 | ★★★☆☆ | ⭐⭐⭐ |
🎯 推荐: 本课程使用 VMware Workstation Pro 17 + Ubuntu 22.04 Server 作为宿主机开发环境。如果你需要模拟 ARM 目标板,请额外安装 QEMU。
1.2.2 虚拟机安装步骤
- 下载 Ubuntu 22.04 LTS Server ISO 镜像(约 2GB)。
- 创建新虚拟机:选择 Linux → Ubuntu 64-bit,分配 4 核 CPU、8GB 内存、80GB 磁盘。
- 网络模式选择 桥接模式,使虚拟机获得独立 IP,方便后续 scp 传输。
- 安装完成后,执行
sudo apt update && sudo apt upgrade -y。 - 安装必要开发包:
sudo apt install build-essential git vim libncurses-dev flex bison libssl-dev libelf-dev。
1.3 交叉编译工具链配置
1.3.1 为什么需要交叉编译?
嵌入式目标板(如 ARM、RISC-V)资源有限,无法直接运行 gcc 编译大型内核。因此我们在性能强劲的 x86 宿主机上使用交叉编译器,生成目标架构的可执行文件。
1.3.2 安装 ARM 交叉工具链
# 安装 ARM 32位硬浮点交叉工具链
sudo apt install gcc-arm-linux-gnueabihf binutils-arm-linux-gnueabihf
# 验证安装
arm-linux-gnueabihf-gcc --version
# 输出示例:arm-linux-gnueabihf-gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0
1.3.3 工具链清单
| 工具 | 作用 | 示例命令 |
|---|---|---|
| arm-linux-gnueabihf-gcc | C 编译器 | 编译内核与模块 |
| arm-linux-gnueabihf-ld | 链接器 | 链接目标文件 |
| arm-linux-gnueabihf-objcopy | 二进制格式转换 | ELF → 裸二进制 |
| arm-linux-gnueabihf-objdump | 反汇编 | 调试与分析 |
⚠️ 注意: 交叉工具链版本必须与目标内核版本匹配。建议使用 Ubuntu 官方源提供的 gcc-11 或 gcc-12 系列,避免使用过于陈旧或过新的版本。
1.4 内核源码下载与编译
1.4.1 下载内核源码
# 从 kernel.org 下载 Linux 6.1 LTS
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.tar.xz
# 解压
tar -xf linux-6.1.tar.xz
cd linux-6.1
1.4.2 配置内核
内核配置有三种常用方式:
- defconfig:使用架构默认配置,快速启动。
- menuconfig:基于 ncurses 的图形化配置,适合定制。
- 本地配置:从当前系统拷贝
/boot/config-$(uname -r)。
# 使用 ARM 默认配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- vexpress_defconfig
# 或使用 menuconfig 手动调整
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
1.4.3 编译内核与模块
# 编译内核镜像(zImage)与设备树
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage dtbs -j$(nproc)
# 编译内核模块
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules -j$(nproc)
# 安装模块到临时目录
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=./modules modules_install
1.5 第一个内核模块:Hello World
1.5.1 模块源码
创建一个名为 hello.c 的文件,内容如下:
// hello.c — 第一个内核模块
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
static int __init hello_init(void)
{
printk(KERN_INFO "Hello, Linux Kernel Module!\n");
printk(KERN_INFO "Module loaded successfully.\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO "Goodbye, Linux Kernel Module!\n");
printk(KERN_INFO "Module unloaded.\n");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple Hello World kernel module");
MODULE_VERSION("1.0");
1.5.2 Makefile 编写
在同目录下创建 Makefile:
# Makefile — 编译内核模块
obj-m := hello.o
# 指定内核源码路径(根据实际解压路径修改)
KDIR := /home/user/linux-6.1
# 交叉编译前缀(ARM 目标)
CROSS := ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-
all:
$(MAKE) -C $(KDIR) M=$(PWD) $(CROSS) modules
clean:
$(MAKE) -C $(KDIR) M=$(PWD) $(CROSS) clean
1.5.3 编译与测试
# 编译模块
make
# 查看生成的 .ko 文件
ls -l hello.ko
# 将模块传输到目标板(假设目标板 IP 为 192.168.1.100)
scp hello.ko root@192.168.1.100:/root/
# 在目标板上加载模块
insmod hello.ko
# 查看内核日志
dmesg | tail -5
# 输出示例:
# [ 1234.567890] Hello, Linux Kernel Module!
# [ 1234.567895] Module loaded successfully.
# 卸载模块
rmmod hello
# 再次查看日志
dmesg | tail -3
# [ 1234.678901] Goodbye, Linux Kernel Module!
# [ 1234.678905] Module unloaded.
✅ 验证要点:
- 使用
lsmod查看已加载模块列表,确认 hello 模块存在。 - 使用
modinfo hello.ko查看模块作者、描述、许可证等信息。 - 如果
insmod报错Invalid module format,请检查内核版本与编译时使用的源码版本是否一致。
1.6 常见问题与避坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| insmod: Invalid module format | 模块与运行内核版本不匹配 | 使用与目标内核完全相同的源码编译 |
| make: *** 没有规则可制作目标 | KDIR 路径错误或内核未配置 | 检查 KDIR 路径,先执行 make defconfig |
| printk 不输出 | 日志级别被控制台过滤 | 使用 dmesg 查看,或调整 /proc/sys/kernel/printk |
| 交叉编译链接错误 | 工具链版本过旧 | 升级到 gcc-11 以上,或使用 Linaro 工具链 |
⚠️ 重要提醒: 内核模块运行在特权级最高层(Ring 0),一个错误的指针访问就可能导致系统崩溃。建议在虚拟机或开发板上测试,不要在重要生产环境中加载未经测试的模块。
1.7 本章小结
通过本章的学习,你已经完成了 Linux 内核驱动开发环境的完整搭建:
- 安装了 Ubuntu 虚拟机作为开发宿主机。
- 配置了 ARM 交叉编译工具链。
- 下载并编译了 Linux 6.1 内核源码。
- 编写、编译、加载并卸载了第一个内核模块 Hello World。