1. 驱动开发概述

1.1 设备驱动的作用

设备驱动是操作系统内核与硬件设备之间的“翻译官”与“管理者”。它向上为内核提供统一的文件操作接口(如 open、read、write、ioctl),向下直接操作硬件寄存器、中断控制器和 DMA 引擎。没有驱动,操作系统无法感知键盘、网卡、磁盘等任何外设的存在。

核心职责: 将硬件特定的寄存器操作、中断响应、数据搬移封装为内核可调用的标准接口,同时保证并发访问下的数据一致性与实时性。
设备驱动在系统中的桥梁位置 用户空间 应用程序 / 系统调用 内核空间 VFS / 文件子系统 设备驱动 中断处理 / DMA / 寄存器 硬件设备 网卡 / 磁盘 / 传感器 系统调用接口(read/write/ioctl) 驱动是唯一能安全访问硬件寄存器的内核模块,中断与 DMA 由驱动直接管理

1.2 内核空间与用户空间

现代操作系统通过 CPU 特权级将内存划分为两个隔离区域,以保障系统安全与稳定。Linux 使用 0 级(内核态)和 3 级(用户态),驱动运行在内核空间,应用程序运行在用户空间。

对比维度 内核空间 用户空间
CPU 特权级 Ring 0(最高权限) Ring 3(受限权限)
内存访问 直接访问物理内存与硬件寄存器 仅能访问虚拟地址,通过系统调用陷入内核
崩溃影响 导致整个系统崩溃(内核 panic) 仅终止当前进程,不影响其他进程
中断响应 直接处理硬件中断,运行中断服务例程 无法直接响应中断,需内核转发信号
典型代码 驱动模块、文件系统、调度器 Shell、GUI、Web 服务器
开发要点: 驱动开发中必须使用内核 API(如 kmalloc、printk),不能调用标准 C 库函数(如 malloc、printf)。用户空间与内核空间的数据传递需通过 copy_from_user / copy_to_user 等专用函数。
内核空间与用户空间隔离模型 用户空间 进程 A / 进程 B / 进程 C 虚拟地址空间(4GB 每进程) 内核空间 驱动模块 / 调度器 / 内存管理 物理地址空间(直接映射) 系统调用接口 open / read / write / ioctl copy_from_user / copy_to_user 用户空间通过系统调用陷入内核,驱动在内核空间完成硬件操作后返回结果

1.3 驱动开发环境搭建

嵌入式驱动开发通常采用“宿主机编译 + 目标机运行”的交叉开发模式。宿主机(x86 PC)安装交叉编译工具链和内核源码树,编译生成目标架构(ARM/RISC-V/MIPS)的可执行模块,再部署到目标板运行。

1.3.1 交叉编译工具链

交叉编译工具链包含针对目标架构的编译器、链接器、汇编器和库文件。以 ARM 架构为例,常用工具链为 arm-linux-gnueabihf- 系列。

工具组件 命令示例 作用
C 编译器 arm-linux-gnueabihf-gcc 将 C 源码编译为目标架构的汇编或目标文件
汇编器 arm-linux-gnueabihf-as 将汇编代码转换为机器码
链接器 arm-linux-gnueabihf-ld 将多个目标文件链接为内核模块或可执行文件
目标文件工具 arm-linux-gnueabihf-objdump 反汇编、查看符号表、段信息
常见陷阱: 使用宿主机原生 gcc 编译的 .ko 模块无法在目标板加载,必须使用与目标内核版本匹配的交叉工具链。工具链的 glibc 版本也需与目标系统一致,否则会出现段错误或符号未定义。

1.3.2 内核源码树

编译内核模块需要完整的内核源码树,包含头文件、Makefile 和编译配置。源码树通常位于 /lib/modules/$(uname -r)/build/ 或自定义路径。

典型的内核模块 Makefile 结构如下:

# Makefile 示例:编译一个简单的字符设备驱动
obj-m := hello_driver.o

# 指定内核源码树路径
KDIR := /home/developer/linux-5.10.100

# 交叉编译工具链前缀
CROSS_COMPILE := arm-linux-gnueabihf-

all:
	$(MAKE) -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) modules

clean:
	$(MAKE) -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) clean
交叉编译环境搭建流程 宿主机(x86) 交叉工具链 内核源码树 编译 .ko 模块 目标板(ARM) 提供编译环境 与 Makefile 规则 提供内核头文件 与模块编译框架

1.3.3 环境验证步骤

  1. 安装交叉工具链: 从 Linaro 或芯片厂商官网下载预编译工具链,解压至 /opt/toolchains/,并添加至 PATH 环境变量。
  2. 准备内核源码树: 下载与目标板相同版本的内核源码,执行 make ARCH=arm defconfig 生成默认配置,再执行 make ARCH=arm menuconfig 按需调整。
  3. 编译测试模块: 编写一个简单的 hello_driver.c,使用上述 Makefile 编译,生成 hello_driver.ko。
  4. 部署与加载: 通过 NFS、TFTP 或 U 盘将 .ko 文件传输至目标板,执行 insmod hello_driver.ko 加载,lsmod 查看模块列表,rmmod hello_driver 卸载。
快速验证命令:
# 查看目标板内核版本
uname -r
# 查看已加载模块
lsmod
# 查看内核打印信息(驱动调试常用)
dmesg | tail