Linux内核中的锁机制对比:选择合适的同步原语

张开发
2026/4/5 20:56:56 15 分钟阅读

分享文章

Linux内核中的锁机制对比:选择合适的同步原语
Linux内核中的锁机制对比选择合适的同步原语作为一名深耕操作系统和嵌入式开发的工程师我对Linux内核中的各种锁机制有着深入的理解。不同的锁适用于不同的场景选择合适的锁对于系统性能至关重要。内核锁的类型1. 互斥锁Mutex互斥锁是最常用的睡眠锁// 定义和初始化 DEFINE_MUTEX(my_mutex); struct mutex my_mutex; mutex_init(my_mutex); // 加锁和解锁 mutex_lock(my_mutex); // 临界区 mutex_unlock(my_mutex); // 尝试加锁非阻塞 if (mutex_trylock(my_mutex)) { // 成功获取锁 mutex_unlock(my_mutex); } // 可中断加锁 if (mutex_lock_interruptible(my_mutex)) return -ERESTARTSYS;适用场景临界区可能睡眠长时间持有锁用户空间接口2. 自旋锁Spinlock自旋锁是忙等待锁适用于短临界区// 定义和初始化 DEFINE_SPINLOCK(my_lock); spinlock_t my_lock; spin_lock_init(my_lock); // 基本使用 spin_lock(my_lock); // 临界区 spin_unlock(my_lock); // 保存中断状态 unsigned long flags; spin_lock_irqsave(my_lock, flags); // 临界区 spin_unlock_irqrestore(my_lock, flags); // 禁用软中断 spin_lock_bh(my_lock); // 临界区 spin_unlock_bh(my_lock);适用场景临界区很短中断上下文不能睡眠的上下文3. 读写锁RW Lock读写锁允许多个读者或一个写者// 读写自旋锁 DEFINE_RWLOCK(my_rwlock); // 读锁 read_lock(my_rwlock); // 读临界区 read_unlock(my_rwlock); // 写锁 write_lock(my_rwlock); // 写临界区 write_unlock(my_rwlock); // 读写信号量 DECLARE_RWSEM(my_rwsem); down_read(my_rwsem); // 读临界区 up_read(my_rwsem); down_write(my_rwsem); // 写临界区 up_write(my_rwsem);适用场景读多写少读操作可以并发4. RCURead-Copy-UpdateRCU提供无锁的读操作// 读端 rcu_read_lock(); p rcu_dereference(ptr); // 使用p rcu_read_unlock(); // 写端 new_p kmalloc(...); *new_p *old_p; new_p-field new_value; rcu_assign_pointer(ptr, new_p); synchronize_rcu(); kfree(old_p);适用场景读多写极少对读性能要求极高锁的选择决策树是否需要睡眠 ├── 是 → 使用互斥锁 └── 否 → 临界区是否很短 ├── 是 → 使用自旋锁 └── 否 → 读多写少 ├── 是 → 使用读写锁 └── 否 → 读极多写极少 ├── 是 → 使用RCU └── 否 → 使用自旋锁性能对比锁类型获取开销释放开销并发性内存占用原子操作极低极低极好4-8字节自旋锁低低好4字节互斥锁中中一般较大读写锁低/中低/中读好写差较大RCU极低低读极好较大常见陷阱1. 在持有自旋锁时睡眠// 错误持有自旋锁时睡眠 spin_lock(my_lock); schedule(); // 严重错误 spin_unlock(my_lock); // 正确使用互斥锁 mutex_lock(my_mutex); schedule(); mutex_unlock(my_mutex);2. 锁的顺序不当导致死锁// 线程1 spin_lock(lock_a); spin_lock(lock_b); // 可能死锁 // 线程2 spin_lock(lock_b); spin_lock(lock_a); // 可能死锁 // 正确统一锁顺序 spin_lock(lock_a); spin_lock(lock_b);3. 递归加锁// 普通互斥锁不支持递归 mutex_lock(my_mutex); mutex_lock(my_mutex); // 死锁 // 使用递归互斥锁如果确实需要 struct mutex my_mutex; mutex_init(my_mutex); // Linux内核mutex不支持递归需要自行实现或使用信号量锁的调试1. 锁验证器Lockdep# 启用Lockdep CONFIG_LOCKDEPy CONFIG_LOCK_STATy # 查看锁依赖关系 cat /proc/lockdep # 查看锁统计信息 cat /proc/lock_stat2. 死锁检测// Lockdep会自动检测潜在的死锁 // 如ABBA依赖关系性能优化建议减少锁的粒度将大锁拆分为多个小锁减少锁的持有时间只保护必要的代码使用无锁算法对于简单场景使用原子操作或RCU避免在热路径加锁使用Per-CPU变量等技术考虑锁的公平性长时间等待的线程应该优先获得锁总结Linux内核提供了丰富的锁机制每种锁都有其适用场景。作为嵌入式开发者理解各种锁的特性和开销选择合适锁机制对于构建高性能、可靠的系统至关重要。

更多文章