PREEMPT_RT下local_lock与锁组合使用的技术疑问
Linux内核锁相关疑问解答
背景说明
近期学习Linux内核锁相关内容,了解到以下关键信息:
- 在非PREEMPT_RT内核中,代码序列:
完全等价于:local_lock_irq(&local_lock); raw_spin_lock(&lock);raw_spin_lock_irq(&lock); - 但上述序列在PREEMPT_RT内核中会失效,原因是
local_lock_irq()被映射为不关闭中断和抢占的per-CPU spinlock_t; - 而代码序列:
在两种内核中均可正常工作。local_lock_irq(&local_lock); spin_lock(&lock);
已知PREEMPT_RT内核的实现细节:
local_lock_irq通过migrate_disable和per-CPU自旋锁实现,不关闭中断和抢占;spin_lock基于RT_MUTEX实现;- IRQ以中断线程形式运行。
疑问解答
1. 第一个代码序列在PREEMPT_RT下失效,是否因执行local_lock_irq后可能被中断线程抢占?
是的,核心原因就是执行local_lock_irq后可能被中断线程抢占,进而引发死锁:
- PREEMPT_RT下,
local_lock_irq仅禁止任务迁移、保护当前CPU上的临界区,但不关闭中断,也不禁止抢占。中断以线程形式运行,优先级通常高于普通线程,因此会抢占当前持有local_lock_irq的执行流; raw_spin_lock在PREEMPT_RT下仍为自旋锁,获取时会禁用抢占。若被抢占的线程已持有raw_spin_lock,中断线程若尝试获取同一个raw_spin_lock,会进入自旋等待状态;但持有锁的线程因被抢占无法继续执行释放锁,最终导致死锁,这就是序列失效的原因。
2. 第二个序列中,非PREEMPT_RT内核下spin_lock等价于raw_spin_lock,为何该序列能在两种内核中正常工作?
这是因为spin_lock在PREEMPT_RT和非PREEMPT_RT下的实现特性,结合local_lock_irq的作用,刚好适配两种场景:
- 非PREEMPT_RT内核:
spin_lock等价于raw_spin_lock,此时序列local_lock_irq(&local_lock); spin_lock(&lock);和raw_spin_lock_irq(&lock);的效果完全一致——local_lock_irq关闭本地CPU中断并防止迁移,spin_lock处理SMP并发,合起来等同于关闭中断的自旋锁,自然能正常工作; - PREEMPT_RT内核:
local_lock_irq仍保证当前CPU上无其他执行流进入临界区,同时允许抢占;spin_lock基于RT_MUTEX实现,获取锁时若无法立即持有,会进入睡眠等待而非自旋。即便当前执行流被中断线程抢占,中断线程若尝试获取同一个spin_lock,会进入睡眠,不会阻塞持有锁的线程后续执行;持有锁的线程被调度回来后,能正常释放锁,中断线程也能被唤醒获取锁,不会出现死锁。
两者配合既满足了本地CPU的临界区保护,又适配了PREEMPT_RT的抢占式设计,因此能正常工作。
内容的提问来源于stack exchange,提问作者weikang guo
相关产品推荐
相关产品推荐

