1. Linux驱动开发概述
1.1 什么是设备驱动
设备驱动是操作系统内核中负责管理特定硬件设备的软件模块。它充当硬件设备与用户应用程序之间的“翻译官”,将应用程序发出的通用操作请求(如读、写、控制)转换为硬件设备能够理解的特定指令序列。
从本质上看,设备驱动实现了以下核心功能:
- 硬件抽象:屏蔽不同硬件设备的差异,向上层提供统一的编程接口。
- 资源管理:管理设备的寄存器、中断、DMA通道、内存映射等硬件资源。
- 数据交换:在用户空间与硬件设备之间高效传输数据。
- 事件通知:处理硬件产生的中断,并通知上层应用程序。
理解要点:没有驱动,操作系统就无法识别和控制硬件设备。在嵌入式Linux系统中,驱动开发是连接软件与硬件的核心环节,也是系统稳定性和性能的关键保障。
1.2 驱动分类:字符设备、块设备与网络设备
Linux内核将设备驱动分为三大基本类型,每种类型对应不同的硬件特性和访问模式:
| 驱动类型 | 典型设备 | 数据访问方式 | 关键特征 |
|---|---|---|---|
| 字符设备 | 串口、GPIO、I2C、SPI、LED、按键 | 顺序字节流,按字符读写 | 无缓冲区,直接操作;通过/dev节点访问 |
| 块设备 | eMMC、NAND Flash、SD卡、硬盘 | 随机块访问,以块为单位 | 有内核缓冲区(page cache);支持文件系统挂载 |
| 网络设备 | 以太网、Wi-Fi、蓝牙、CAN | 数据包(sk_buff)收发 | 不通过/dev节点;通过socket接口访问 |
本课程的核心是字符设备驱动,它是嵌入式Linux驱动开发中最基础、最常用、最适合入门学习的驱动类型。字符设备驱动的结构清晰、接口简单,掌握它之后,学习块设备和网络设备将事半功倍。
课程聚焦:字符设备驱动是嵌入式Linux驱动开发的“Hello World”。从字符设备入手,可以快速理解驱动框架、文件操作接口、内核内存管理、中断处理等核心概念。
1.3 内核空间与用户空间
Linux系统将虚拟内存划分为两个特权级别不同的区域:内核空间和用户空间。这种隔离机制是系统安全性和稳定性的基石。
1.3.1 核心差异对比
| 对比维度 | 内核空间 | 用户空间 |
|---|---|---|
| 运行级别 | 特权模式(Ring 0 / EL1) | 非特权模式(Ring 3 / EL0) |
| 访问权限 | 可访问所有硬件资源和内存 | 只能访问受限内存,不能直接操作硬件 |
| 崩溃影响 | 内核崩溃 → 系统宕机 | 进程崩溃 → 仅该进程退出 |
| 代码运行 | 内核模块、驱动、核心调度 | 应用程序、库、Shell脚本 |
| 内存地址 | 高地址区域(通常1GB以上) | 低地址区域 |
1.3.2 用户态与内核态的切换
应用程序不能直接调用内核函数或操作硬件。当用户程序需要执行特权操作时,必须通过系统调用(System Call)陷入内核态,由内核代表用户程序完成操作后再返回用户态。常见的系统调用包括:
open()— 打开设备文件read()/write()— 读写数据ioctl()— 设备控制mmap()— 内存映射
驱动开发注意事项:驱动代码运行在内核空间,拥有最高权限。因此驱动中的任何错误(空指针、缓冲区溢出、死锁)都可能导致整个系统崩溃。驱动开发者必须比应用程序开发者更加谨慎。
1.4 驱动开发环境搭建
在开始编写字符设备驱动之前,必须搭建完整的开发环境。嵌入式Linux驱动开发环境的核心组件包括:交叉编译工具链和内核源码树。
1.4.1 交叉编译工具链
嵌入式设备的CPU架构通常与开发主机不同(如ARM、RISC-V、MIPS),因此需要在主机上安装交叉编译工具链,用于生成目标设备上可执行的代码。
常见交叉编译工具链示例:
| 目标架构 | 工具链前缀 | 示例 |
|---|---|---|
| ARM (32位) | arm-linux-gnueabihf- |
arm-linux-gnueabihf-gcc |
| ARM64 (AArch64) | aarch64-linux-gnu- |
aarch64-linux-gnu-gcc |
| RISC-V (64位) | riscv64-linux-gnu- |
riscv64-linux-gnu-gcc |
1.4.2 内核源码树
驱动代码需要依赖内核头文件和编译框架,因此必须准备与目标设备内核版本匹配的内核源码树。源码树通常通过以下方式获取:
- 官方内核源码:从
kernel.org下载主线内核 - 厂商BSP源码:芯片厂商提供的板级支持包(如NXP、TI、Rockchip)
- 开发板源码:开发板厂商提供的定制内核(如Raspberry Pi、BeagleBone)
1.4.3 环境搭建步骤
- 安装交叉编译工具链
# 以ARM 32位为例,使用apt安装 sudo apt-get install gcc-arm-linux-gnueabihf # 验证安装 arm-linux-gnueabihf-gcc --version - 获取并解压内核源码
# 下载内核源码(以5.10版本为例) wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.10.tar.xz tar -xf linux-5.10.tar.xz cd linux-5.10 - 配置内核
# 使用目标设备的默认配置 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig # 或使用厂商提供的配置文件 make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- myboard_defconfig - 编译内核(可选,但建议先编译一次)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j4 - 编译内核模块(生成Module.symvers)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
环境验证:完成上述步骤后,内核源码树根目录下应包含
vmlinux、Module.symvers、include/generated/autoconf.h 等关键文件。这些文件是后续编译驱动模块的基础。
1.4.4 驱动模块编译示例(Makefile)
以下是一个最简单的字符设备驱动模块的Makefile模板,用于验证环境是否搭建成功:
# Makefile - 字符设备驱动模块编译
obj-m := hello_drv.o
KDIR := /home/user/linux-5.10
CROSS := arm-linux-gnueabihf-
all:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS) modules
clean:
make -C $(KDIR) M=$(PWD) ARCH=arm CROSS_COMPILE=$(CROSS) clean
关键变量说明:
obj-m:指定要编译成模块的目标文件KDIR:内核源码树路径CROSS:交叉编译工具链前缀M=$(PWD):指定当前目录为模块源码目录
1.5 本章小结
本章建立了嵌入式Linux驱动开发的基础认知框架:
- 设备驱动是连接硬件与软件的桥梁,负责硬件抽象和资源管理。
- 驱动分为字符设备、块设备和网络设备三大类,本课程聚焦字符设备。
- 内核空间与用户空间的隔离机制保障了系统安全,驱动运行在内核空间。
- 交叉编译工具链和内核源码树是驱动开发的基础环境,必须正确配置。