并发与同步机制面试题:自旋锁、互斥锁、信号量、RCU 使用场景与区别
本专题考察你对 Linux 内核同步原语的理解深度。面试中常从适用场景、睡眠/忙等、上下文限制、性能代价四个维度展开追问,需要你不仅知道区别,更能结合项目经验说明选型理由。
面试问答卡
面试问题:自旋锁与互斥锁在使用场景上有何区别?
考察点:是否理解忙等待 vs 睡眠等待的适用场景;能否区分中断上下文与进程上下文;是否清楚 SMP 与 UP 下的行为差异。
回答框架:先给出核心区别(是否导致睡眠);再按临界区长度 / 上下文 / CPU 数量三个维度对比;最后用一个代码/项目例子说明选型依据。
示例回答:自旋锁忙等待,适合短临界区(<10us)且不可睡眠的中断上下文。互斥锁睡眠等待,适合长临界区、进程上下文。例如在网卡驱动 NAPI 轮询中保护描述符环用自旋锁,而块设备请求队列用互斥锁。
可能追问:① 自旋锁在单核 CPU 上会怎样?② 中断上下文使用自旋锁后如何避免死锁?③ spin_lock_irqsave 与 spin_lock 的差异。
面试问题:信号量与互斥锁能否互相替代?使用场景有何本质区别?
考察点:是否理解信号量的计数量与互斥锁的排他性;能否区分同步 vs 互斥;是否知道信号量在 Linux 内核中的演进(被 mutex 部分替代)。
回答框架:先说不可以替代;原因:互斥锁有所有者概念、支持递归/优先级继承等;信号量更轻量且可用于计数同步;最后问场景举例。
示例回答:互斥锁是排他锁,有 owner、支持 PI(优先级继承),适合保护临界区;信号量可初始化为任意计数,常用于生产者-消费者模式。例如 USB 驱动中 urb 的释放同步使用 completion 类似信号量,而块设备请求队列保护用互斥锁。
可能追问:① 为什么内核后来引入 mutex 而减少 semaphore 的使用?② 说说 down_interruptible 与 down 的区别。③ 信号量能否在中断上下文使用?
面试问题:RCU(Read-Copy-Update)适用于什么场景?与普通读写锁相比有什么优势?
考察点:是否理解 RCU 的无锁读端原理;能否说出 grace period 和宽限期;是否了解 RCU 对数据结构(如链表)的约束;能否讲出性能测试对比。
回答框架:先定义 RCU 核心:读者无需锁、写者通过延迟回收实现更新;再用读多写少场景举例;最后对比读端延迟与写端内存开销。
示例回答:RCU 适合读负载远大于写负载的场景,如路由表、文件系统 dentry 缓存。读端仅需要 rcu_read_lock,几乎无开销;写端通过 rcu_assign_pointer 和 synchronize_rcu 等待所有读者离开后回收旧版本。相比读写锁,读者性能提升 5~10 倍。
可能追问:① RCU 的 grace period 如何实现?② 为什么 RCU 不能保护通用数据结构(如链表插入中间)?③ 如何调试 RCU 死锁或宽限期堆积?
项目案例:多核嵌入式网络驱动并发控制
| 项目名称 | 双千兆以太网驱动并发优化(ARM 双核 Cortex-A72) |
| 业务背景 | 设备作为边缘网关,需要同时处理 2 个 1Gbps 端口的数据转发。多个 CPU 核心共享描述符环(TX/RX ring)、统计计数器、MAC 寄存器等资源,原始驱动使用大粒度自旋锁,导致 CPU 0 软中断时其他核心等待,吞吐量只有 1.2Gbps。 |
| 规模指标 | 4 个 DMA 通道,256 个描述符/通道;吞吐量目标 1.8Gbps(满负载);CPU 主频 1.8GHz;中断合并延迟 < 200us。 |
| 技术栈 | Linux 5.10 LTS;自旋锁 + 关中断版本;RCU 保护路由表;per-CPU 变量 + 原子操作(统计计数器);kmem_cache 分配描述符。 |
| 核心难点 | ① 多个 CPU 同时访问 TX 描述符环导致锁竞争严重;② 统计计数器频繁更新,普通 spinlock 成为瓶颈;③ 路由表需实时更新但读操作占 95% 以上,如何无锁读;④ 中断上下文与进程上下文共享锁时的死锁预防。 |
| 面试高频追问 | ① 为什么选择自旋锁而不是互斥锁?② 如何设计 per-CPU 变量解决统计计数器冲突?③ RCU 与 rwlock 在该项目中的实测性能差异?④ 描述你为每个核心分配专用描述符环的机制。 |
| 可迁移回答话术 | “在项目中,我首先通过 perf top 和 ftrace 定位到锁竞争热点,发现自旋锁在 4 核下占用 30% CPU 空转。于是对读多写少的路由表采用 RCU,读端仅需 rcu_read_lock,写端用 seqlock 串行化更新,吞吐量提升 40%。对计数器使用 per-CPU 变量 + 原子累加,避免写者间的竞争。核心思路是‘区分读写场景,让锁尽可能局部化’。” |