Linux内核驱动实战60例系统讲解 - 第1节

1.1 开发环境全景概览

Linux 内核驱动开发的第一步是搭建一个稳定、可复现的编译与运行环境。本章将引导你从零开始,完成虚拟机安装、交叉编译工具链配置、内核源码下载与编译,并最终编写并加载第一个内核模块——Hello World。

整个环境搭建流程可概括为以下四个阶段:

  1. 宿主机 + 虚拟机:在 Windows/macOS 上通过 VMware 或 VirtualBox 安装 Ubuntu 22.04 LTS。
  2. 交叉编译工具链:安装 arm-linux-gnueabihf- 系列工具,用于在 x86 主机上编译 ARM 架构内核与模块。
  3. 内核源码:从 kernel.org 下载稳定版 Linux 内核,配置并编译出内核镜像与模块。
  4. 第一个模块:编写一个简单的内核模块,使用 Makefile 编译,并在目标系统上加载/卸载。
💡 提示: 本课程所有实验均基于 Linux 6.1 LTS 与 ARM Cortex-A7 平台。如果你使用 x86 虚拟机直接运行,可跳过交叉编译步骤,直接使用本地 gcc 编译。
Linux 内核驱动开发环境架构 宿主机 (x86_64) Ubuntu 22.04 gcc / make / git 虚拟机 (QEMU/VMware) ARM Cortex-A7 Linux 6.1 内核 交叉编译工具链 arm-linux-gnueabihf- gcc / ld / objcopy 内核源码 linux-6.1.tar.xz 编译产物 zImage / modules / dtbs 宿主机编译内核与模块 通过 scp 传输到虚拟机

1.2 虚拟机安装与配置

1.2.1 选择虚拟化方案

方案适用场景性能推荐指数
VMware Workstation ProWindows 桌面用户★★★★★⭐⭐⭐⭐⭐
VirtualBox跨平台、开源★★★★☆⭐⭐⭐⭐
QEMU + libvirtLinux 原生、ARM 模拟★★★☆☆⭐⭐⭐
🎯 推荐: 本课程使用 VMware Workstation Pro 17 + Ubuntu 22.04 Server 作为宿主机开发环境。如果你需要模拟 ARM 目标板,请额外安装 QEMU。

1.2.2 虚拟机安装步骤

  1. 下载 Ubuntu 22.04 LTS Server ISO 镜像(约 2GB)。
  2. 创建新虚拟机:选择 Linux → Ubuntu 64-bit,分配 4 核 CPU、8GB 内存、80GB 磁盘。
  3. 网络模式选择 桥接模式,使虚拟机获得独立 IP,方便后续 scp 传输。
  4. 安装完成后,执行 sudo apt update && sudo apt upgrade -y。
  5. 安装必要开发包: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-gccC 编译器编译内核与模块
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
Linux 内核编译流程 下载源码 linux-6.1.tar.xz 配置 defconfig / menuconfig 编译内核 make zImage dtbs 编译模块 make modules 输出产物 arch/arm/boot/zImage 输出产物 modules/lib/modules/6.1.0/

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。