锁类型及其规则¶

锁类型及其规则¶

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 的自旋锁来实现。

评论留言