1. Linux驱动概述
设备驱动是操作系统与硬件设备之间的“翻译官”。在 Linux 系统中,驱动以内核模块的形式存在,负责将用户空间的请求转换为硬件可执行的指令序列。理解驱动在内核中的角色以及用户空间与内核空间的边界,是编写正确、高效驱动的第一步。
1.1 什么是设备驱动
设备驱动是一组控制特定硬件设备的函数和数据结构的集合。它封装了硬件操作细节,向上层提供统一的接口。Linux 将设备分为三大类,每类对应不同的驱动模型:
| 设备类型 | 典型设备 | 驱动接口 | 设备文件 |
|---|---|---|---|
| 字符设备 | 串口、GPIO、I2C 传感器 | open/read/write/ioctl | /dev/ttyS0, /dev/gpio0 |
| 块设备 | 硬盘、SD 卡、U 盘 | 块请求队列 + bio | /dev/sda, /dev/mmcblk0 |
| 网络设备 | 以太网卡、WiFi 模块 | socket + sk_buff | eth0, wlan0(无设备文件) |
课程聚焦: 本课程以字符设备驱动为主线,因为它结构清晰、接口简单,最适合初学者理解驱动框架。掌握字符设备后,块设备和网络设备的原理可以触类旁通。
1.2 驱动在内核中的角色
驱动在内核中承担三个核心角色:
- 硬件抽象层: 将不同厂商、不同接口的硬件差异隐藏在内核中,用户程序无需关心硬件细节。
- 资源管理者: 管理中断、DMA、寄存器、内存映射等硬件资源,防止多个进程同时访问导致冲突。
- 数据通道: 在用户空间与硬件之间搬运数据,必要时进行格式转换、校验或协议解析。
下图展示了驱动在内核中的位置与数据流向:
关键理解: 驱动是内核的一部分,运行在内核空间,拥有最高权限。它不能直接调用用户空间的库函数(如 printf),只能使用内核 API(如 printk)。驱动代码中的错误会直接导致内核崩溃(Oops),而用户程序崩溃只会影响自身进程。
1.3 用户空间与内核空间的区别
Linux 通过 CPU 的特权级机制将内存划分为两个区域。x86 架构使用 Ring 0(内核空间)和 Ring 3(用户空间),ARM 架构使用 SVC 模式和 USR 模式。两者的核心区别如下:
| 对比维度 | 用户空间 | 内核空间 |
|---|---|---|
| CPU 特权级 | Ring 3(受限模式) | Ring 0(最高权限) |
| 可访问内存 | 仅自身虚拟地址空间 | 全部物理内存 + 内核虚拟空间 |
| 可执行指令 | 普通指令,无 I/O 指令 | 所有指令,包括 in/out、cli/sti |
| 直接访问硬件 | 不允许 | 允许(通过 MMIO / PIO) |
| 崩溃影响 | 仅终止当前进程 | 系统 panic 或 Oops |
| 调试方式 | gdb 直接调试 | kgdb / printk / ftrace |
| 内存分配 | malloc / free | kmalloc / kfree / vmalloc |
用户程序要访问硬件,必须通过系统调用陷入内核。这个过程涉及上下文切换:
- 用户程序调用库函数(如 read())。
- 库函数触发软中断(x86 的 int 0x80 或 syscall 指令)。
- CPU 切换到内核栈,保存用户态寄存器。
- 内核根据系统调用号执行对应的驱动函数。
- 驱动操作硬件,返回结果。
- 恢复用户态寄存器,返回用户空间。
性能代价: 每次系统调用都会发生用户态与内核态的切换,开销约在 50–200 ns(取决于 CPU 架构)。高频 I/O 场景(如网络包处理)应尽量减少系统调用次数,或使用 mmap、splice 等零拷贝技术。
1.4 为什么驱动开发需要理解空间划分
初学者最容易犯的错误就是混淆用户空间与内核空间的编程环境。以下对比表可以帮助你建立正确的思维模型:
| 编程要素 | 用户空间 | 内核空间(驱动) |
|---|---|---|
| 头文件 | <stdio.h> <stdlib.h> <unistd.h> | <linux/module.h> <linux/fs.h> <linux/uaccess.h> |
| 打印输出 | printf() | printk() |
| 内存分配 | malloc() / calloc() | kmalloc() / kzalloc() |
| 字符串操作 | strcpy() / strlen() | strcpy() / strlen()(可用,但需注意) |
| 浮点运算 | 完全支持 | 禁止(内核不保存浮点寄存器) |
| 睡眠/等待 | sleep() / usleep() | msleep() / wait_event_interruptible() |
| 锁机制 | pthread_mutex_t | spinlock_t / mutex(内核版本) |