【Note】《Linux 内核深度解析:基于 ARM64 架构的 Linux 4.x 内核》 第五章:内核互斥技术
《Linux 内核深度解析:基于 ARM64 架构的 Linux 4.x 内核》 第五章:内核互斥技术
在支持多核、抢占、异步中断的 Linux 内核中,如何正确、安全地访问共享资源,是稳定性与性能的核心问题。
5.1 信号量(Semaphore)
-
Linux 信号量是计数型锁,支持多个任务通过
down()/up()机制协调资源访问。 -
可睡眠等待,适合访问较慢设备或长时间资源持有的场景。
-
核心结构体:
struct semaphore {raw_spinlock_t lock;unsigned int count;struct list_head wait_list; };
5.2 读写信号量(RW Semaphore)
-
支持多个读者同时访问资源,写者需独占。
-
适合读多写少的共享数据,如 inode 属性。
-
API 示例:
down_read(); // 读锁 up_read(); down_write(); // 写锁 up_write();
5.3 互斥锁(Mutex)
- 是轻量级睡眠锁,适用于线程间互斥。
- 和信号量不同,mutex 只能由加锁者解锁。
- ARM64 上
mutex_lock()可进入睡眠,使用等待队列实现。
5.4 实时互斥锁(RT Mutex)
- 专为 RT_PREEMPT 实时内核设计。
- 可防止优先级反转(使用优先级继承 PI)。
- 用于 futex、实时线程调度器中。
5.5 原子变量(atomic_t)
-
对整数变量提供无锁原子操作,适合状态位、引用计数等。
-
ARM64 使用
LDXR/STXR实现原子性。 -
示例:
atomic_inc(&val); if (atomic_dec_and_test(&val)) { ... }
5.6 自旋锁(Spinlock)
- 在无法睡眠的上下文(如中断)中使用的基本锁机制。
spin_lock()会忙等待,直到锁可用。- 适合临界区很短的情况;支持
irqsave。
5.7 读写自旋锁(rwlock)
- 多个读者可并发,写者独占。
- 比普通自旋锁更高效,但结构复杂。
- ARM64 实现通过
atomic64_t控制锁状态。
5.8 顺序锁(seqlock)
5.8.1 完整版顺序锁
- 写操作加锁,同时递增序列号;
- 读操作可无锁,读取前后对比序号是否一致。
5.8.2 简化版顺序锁(seqcount)
- 仅记录序列号,无实际锁;
- 适合嵌入其他锁中,如 timekeeper 结构。
5.9 禁止内核抢占(preempt_disable)
preempt_disable()阻止当前任务被抢占;- 不会屏蔽中断,仅用于保护短期临界区;
- 与 spinlock 配合使用,常用于内核调度相关代码。
5.10 进程与软中断互斥
- 软中断不会抢占进程上下文;
- 通过
local_bh_disable()屏蔽本地软中断; - 通常在
netif_rx()等网络函数中使用。
5.11 进程与硬中断互斥
- 使用
spin_lock_irqsave()同时关闭本地中断并加锁; - 硬中断可能在任意时刻打断执行,必须保证一致性。
5.12 每处理器变量(per-CPU Variable)
5.12.1 静态 per-CPU
- 使用
DEFINE_PER_CPU()声明; - 编译时确定,效率高。
5.12.2 动态 per-CPU
- 使用
alloc_percpu()分配; - 适用于运行时可变数量对象。
5.12.3 访问 per-CPU
- 通过
this_cpu_ptr()获取当前 CPU 的对应变量; - 访问效率高,无需加锁。
5.13 每处理器计数器(per-CPU counter)
- 用于高频递增/递减操作(如页缓存引用);
- 结合全局总和与本地计数实现。
percpu_counter_add(&pc, 1);
percpu_counter_sum(&pc);
5.14 内存屏障(Memory Barrier)
5.14.1 编译器屏障(barrier())
- 防止 GCC 等优化器重排指令。
5.14.2 处理器内存屏障
- 使用
smp_mb(),smp_rmb(),smp_wmb(); - 确保多核 CPU 中执行顺序一致。
5.14.3 MMIO 写屏障
- 对设备寄存器写操作顺序控制;
- ARM64 提供
wmb()等。
5.14.4 隐含内存屏障
- 如
spin_unlock()、mutex_unlock()隐含内存屏障; - 一般不需要额外加
mb()。
5.14.5 ARM64 处理器屏障
-
ARM64 支持:
dsb sy:系统范围屏障dmb ish:内部共享屏障isb:指令同步屏障
5.15 RCU(Read-Copy-Update)
5.15.1 使用方法
rcu_read_lock(),rcu_read_unlock()保护读路径;- 更新路径复制数据结构并用
call_rcu()延迟释放。
5.15.2 技术原理
- 利用“宽限期”(Grace Period)确保所有旧读者退出后才清理;
- 使用 quiescent state + callback 队列实现;
- 性能极高,适用于高并发读。
5.16 可睡眠 RCU(SRCU)
5.16.1 使用方法
- 与 RCU 相似,但读路径可睡眠;
- 适合 block 层、文件系统等可能阻塞的上下文。
5.16.2 技术原理
- 维护每 CPU 两个引用计数器;
- 延迟 callback 直到所有读者退出两个 epoch。
5.17 死锁检测工具 lockdep
5.17.1 使用方法
- 开启
CONFIG_LOCKDEP; - 自动检测死锁链、锁顺序问题;
- 适用于调试与内核开发。
5.17.2 技术原理
- 为每个锁维护依赖图;
- 记录加锁路径,通过图搜索检测环路。
总结
Linux 内核互斥机制种类繁多,从基础的信号量、自旋锁,到复杂的 RCU、顺序锁,再到每处理器变量与内存屏障,构建起了内核安全运行的完整“并发保障体系”。
| 分类 | 示例机制 | 特性说明 |
|---|---|---|
| 可睡眠锁 | mutex, semaphore | 用于线程上下文,可阻塞 |
| 忙等锁 | spinlock, rwlock | 不可睡眠,适用于中断、原子操作 |
| 无锁机制 | atomic_t, per-CPU | 高性能,适用于简化数据共享 |
| 高级同步 | RCU, seqlock | 读高并发、写复杂 |
| 工具支持 | lockdep | 自动死锁检测 |