1. 为什么Linux驱动需要关注并发与竞争
在Linux内核开发中,驱动程序的并发与竞争问题就像城市交通中的十字路口——当多个车辆(进程/线程)同时试图通过同一个路口(共享资源)时,如果没有合理的信号灯控制(同步机制),就必然会发生碰撞事故(数据竞争)。我曾在开发一个GPIO控制器驱动时,因为没有处理好中断上下文和进程上下文的并发访问,导致系统随机崩溃,花了整整三天才定位到这个隐蔽的竞态条件。
现代Linux系统普遍具备以下特征,使得并发成为驱动开发无法回避的问题:
- 多核SMP架构成为主流,真正的并行执行成为常态
- 中断处理、内核线程、用户进程可能同时操作同一硬件寄存器
- 内核抢占机制使得驱动代码可能在任意位置被切换
- 设备可能同时被多个进程打开和使用
关键认知:Linux驱动本质上是一个共享资源管理器,必须假设自己随时可能被多个执行路径同时调用。任何对共享数据或硬件资源的非原子访问,都可能成为定时炸弹。
2. 典型竞态场景与危害实例分析
2.1 中断与进程上下文的资源竞争
在开发一个SPI设备驱动时,我曾遇到过这样的场景:用户进程通过read()读取传感器数据时,设备中断突然触发更新了数据缓冲区。没有保护的情况下,可能导致进程读取到部分更新的数据。这种"撕裂读"(torn read)现象在示波器抓取的信号波形中表现为明显的毛刺。
c复制// 有问题的简化代码示例
static irqreturn_t interrupt_handler(int irq, void *dev_id) {
hardware_read(data_buffer); // 中断中更新数据
return IRQ_HANDLED;
}
ssize_t dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) {
copy_to_user(buf, data_buffer, count); // 进程上下文读取
return count;
}
2.2 多核并行引发的缓存一致性问题
在ARM多核平台上调试一个DMA控制器驱动时,我发现当CPU0修改描述符链表后,CPU1有时看不到最新改动。这是因为:
1.
