Linux内核设备驱动开发实战 - 第1节

1.1 Linux 内核版本演进

Linux 内核自 1991 年由 Linus Torvalds 发布以来,经历了从个人项目到支撑全球 IT 基础设施的演进。理解版本号规则与重要里程碑,是选择内核基线、评估功能支持的基础。

版本号命名规则

Linux 内核采用三段式版本号:A.B.C。早期(2.6 之前)偶数为稳定版、奇数为开发版;自 2.6.0 起引入时间驱动模型,不再区分奇偶;2017 年后采用 长期支持(LTS) 与 主线(Mainline) 双轨制。

版本段含义示例
A主版本号,重大架构或 API 变更5.x → 6.x
B次版本号,新特性、驱动、子系统更新5.10 → 5.15
C修订号,Bug 修复、安全补丁5.10.1 → 5.10.2

重要里程碑

  • v0.01 (1991):首个公开版本,仅支持 i386 单任务。
  • v1.0 (1994):支持网络协议栈、虚拟文件系统。
  • v2.6 (2003):引入 udev、sysfs、抢占式内核、CFS 调度器。
  • v3.0 (2011):版本号跳转,无重大架构变化,主要简化命名。
  • v4.x (2015):eBPF 基础设施、livepatch、overlayfs。
  • v5.x (2019):Rust 实验性支持、io_uring、cgroup v2。
  • v6.x (2022):Rust 正式集成、Btrfs 性能优化、ACPI 重构。
开发建议:嵌入式产品选型优先选择 LTS 版本(如 5.10 LTS、6.1 LTS),可获得至少 6 年安全维护,避免频繁主线升级带来的驱动适配成本。
Linux 内核版本演进时间线 v0.01 1991 v1.0 1994 v2.6 2003 v3.0 2011 v5.x 2019 v6.x 2022 LTS 版本每 2–3 年发布一次,嵌入式开发优先选择 LTS 基线

1.2 内核架构:宏内核 vs 微内核

Linux 采用 宏内核(Monolithic Kernel) 设计,所有核心服务(进程调度、内存管理、文件系统、网络协议栈、设备驱动)运行在同一个内核地址空间。这与 微内核(Microkernel) 形成鲜明对比,后者仅保留最小特权级服务,其余功能通过用户态进程通信(IPC)实现。

架构对比

对比维度宏内核(Linux)微内核(如 Minix、QNX)
服务运行位置全部在内核空间内核最小化,服务在用户空间
模块间通信函数直接调用IPC 消息传递
性能高(无上下文切换开销)较低(频繁 IPC 切换)
驱动开发复杂度需注意内核 API 与并发安全驱动可像普通程序开发
故障隔离驱动崩溃可能导致整个系统宕机驱动崩溃可单独重启
典型代表Linux、FreeBSDQNX、Minix、seL4
关键理解:Linux 虽然是宏内核,但通过 可加载内核模块(LKM) 机制实现了运行时动态加载/卸载驱动,兼顾了宏内核的性能与微内核的部分灵活性。设备驱动开发主要工作就是编写 LKM。
宏内核架构(Linux) 微内核架构 内核空间(Kernel Space) 进程调度 内存管理 文件系统 网络协议栈 设备驱动(LKM) 系统调用接口 用户空间(User Space) 微内核(最小特权级) IPC / 地址空间 / 线程调度 文件系统服务 网络协议栈 设备驱动 内存管理服务 用户空间(User Space) 所有服务直接函数调用,性能高 服务通过 IPC 通信,隔离性好

1.3 内核源码目录结构解析

Linux 内核源码采用分层、模块化的目录组织。理解顶层目录的职责,能帮助开发者快速定位驱动框架、核心 API 与构建系统文件。

顶层目录一览

目录/文件功能描述驱动开发者关注度
arch/体系结构相关代码(x86、ARM、RISC-V 等)⭐⭐⭐(移植时需要)
block/块设备层核心(I/O 调度、bio 框架)⭐⭐(块驱动相关)
crypto/加密 API 与算法实现⭐(可选依赖)
Documentation/内核文档、API 说明、设备树绑定⭐⭐⭐⭐⭐(必读)
drivers/设备驱动代码(按子系统分类)⭐⭐⭐⭐⭐(核心工作区)
fs/文件系统实现(ext4、btrfs、proc、sysfs 等)⭐⭐(VFS 接口相关)
include/内核头文件(API 声明、数据结构)⭐⭐⭐⭐⭐(频繁引用)
init/内核初始化流程(main.c、Kconfig)⭐(启动流程理解)
ipc/进程间通信(信号量、消息队列、共享内存)⭐
kernel/核心子系统(调度、锁、时间、printk)⭐⭐⭐(依赖核心 API)
lib/内核通用库函数(字符串、CRC、红黑树)⭐⭐(工具函数)
mm/内存管理(页分配、slab、VMA)⭐⭐(内存分配接口)
net/网络协议栈与网络设备驱动框架⭐⭐⭐(网络驱动相关)
scripts/构建工具、Kconfig 解析、DT 编译脚本⭐⭐(构建配置)
sound/音频子系统(ALSA 框架)⭐⭐(音频驱动)
usr/initramfs 生成相关⭐
virt/虚拟化支持(KVM)⭐

drivers/ 子目录速查

drivers/ 是设备驱动开发的主战场,按子系统进一步细分:

  • drivers/gpio/ — GPIO 控制器驱动
  • drivers/i2c/ — I2C 总线与从设备驱动
  • drivers/spi/ — SPI 总线驱动
  • drivers/pinctrl/ — 引脚控制子系统
  • drivers/regulator/ — 电压调节器驱动
  • drivers/clk/ — 时钟框架驱动
  • drivers/dma/ — DMA 引擎驱动
  • drivers/input/ — 输入设备(触摸、按键、鼠标)
  • drivers/media/ — 摄像头、视频、V4L2 驱动
  • drivers/net/ — 网络接口控制器驱动
  • drivers/mtd/ — 存储类设备(NAND、NOR Flash)
  • drivers/usb/ — USB 主机/设备/控制器驱动
  • drivers/platform/ — 平台设备驱动(常用于嵌入式 SoC)
常见误区:初学者常直接修改 arch/arm/boot/dts/ 下的设备树文件,却忽略 drivers/ 中对应的驱动代码。设备树只描述硬件拓扑,真正的驱动逻辑在 drivers/ 子目录中实现。两者必须协同修改。
Linux 内核源码顶层目录结构 linux/ arch/ drivers/ include/ kernel/ mm/ fs/ drivers/ 子目录(部分) gpio/ i2c/ spi/ pinctrl/ input/ net/ usb/ 驱动开发者重点关注:drivers/(驱动代码)、include/(API 头文件)、Documentation/(开发文档) 构建配置:scripts/(Kconfig 解析)、顶层 Kconfig / Makefile 设备树文件位于 arch/arm/boot/dts/ 或 arch/riscv/boot/dts/,描述硬件拓扑 驱动与设备树通过 compatible 字符串匹配,驱动中需提供 of_match_table

1.4 内核构建系统与 Kconfig

内核使用 Kconfig 语言管理配置选项,配合 Makefile 实现条件编译。驱动开发者需要在驱动目录下添加 Kconfig 条目和 Makefile 编译规则。

驱动 Kconfig 示例

# drivers/gpio/Kconfig
config GPIO_MYDRIVER
    tristate "My GPIO Driver"
    depends on GPIOLIB
    help
      This is a sample GPIO driver for learning purpose.
      Say Y here to compile it into the kernel,
      say M to build it as a module.

驱动 Makefile 示例

# drivers/gpio/Makefile
obj-$(CONFIG_GPIO_MYDRIVER) += gpio-mydriver.o
编译方式:
make menuconfig — 基于 ncurses 的图形化配置界面
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- — 交叉编译内核
make modules — 仅编译内核模块
make modules_install INSTALL_MOD_PATH=/path/to/rootfs — 安装模块到根文件系统

1.5 本章小结

  • Linux 内核版本遵循 A.B.C 规则,嵌入式开发优先选择 LTS 版本。
  • Linux 采用 宏内核 架构,通过 LKM 机制实现驱动动态加载,兼顾性能与灵活性。
  • 内核源码顶层目录中,drivers/、include/、Documentation/ 是驱动开发者的核心工作区。
  • 驱动代码通过 Kconfig 和 Makefile 接入内核构建系统,支持静态编译(Y)与模块编译(M)。