spinlock_t 和 rwlock_t¶
PREEMPT_RT 内核上 spinlock_t 和 rwlock_t 语义的更改有一些含义。 例如,在非 PREEMPT_RT 内核上,以下代码序列按预期工作
local_irq_disable();
spin_lock(&lock);
并且与以下代码完全等效
spin_lock_irq(&lock);
同样适用于 rwlock_t 和 _irqsave() 后缀变体。
在 PREEMPT_RT 内核上,此代码序列会中断,因为 RT-mutex 需要完全可抢占的上下文。 而是使用 spin_lock_irq() 或 spin_lock_irqsave() 及其解锁对应项。 如果中断禁用和锁定必须保持分离,PREEMPT_RT 会提供一种 local_lock 机制。 获取 local_lock 会将任务固定到 CPU,从而允许获取每个 CPU 的中断禁用锁之类的东西。 但是,仅应在绝对必要时使用此方法。
典型的场景是线程上下文中每个 CPU 变量的保护
struct foo *p = get_cpu_ptr(&var1);
spin_lock(&p->lock);
p->count += this_cpu_read(var2);
这是非 PREEMPT_RT 内核上的正确代码,但在 PREEMPT_RT 内核上,这会中断。spinlock_t 语义的 PREEMPT_RT 特定更改不允许获取 p->lock,因为 get_cpu_ptr() 隐式禁用抢占。 以下替换适用于两个内核
struct foo *p;
migrate_disable();
p = this_cpu_ptr(&var1);
spin_lock(&p->lock);
p->count += this_cpu_read(var2);
migrate_disable() 确保任务固定在当前 CPU 上,这反过来保证了对 var1 和 var2 的每个 CPU 的访问在任务保持可抢占的同时停留在同一个 CPU 上。
migrate_disable() 替换对于以下场景无效
func()
{
struct foo *p;
migrate_disable();
p = this_cpu_ptr(&var1);
p->val = func2();
这会中断,因为 migrate_disable() 不能防止来自抢占任务的重入。 这种情况的正确替换是
func()
{
struct foo *p;
local_lock(&foo_lock);
p = this_cpu_ptr(&var1);
p->val = func2();
在非 PREEMPT_RT 内核上,这通过禁用抢占来防止重入。 在 PREEMPT_RT 内核上,这通过获取底层每个 CPU 的自旋锁来实现。
评论留言