内核自旋锁在释放锁之前启用抢占

时间:2015-05-05 12:36:42

标签: linux linux-kernel kernel locking scheduling

当我和一些同事讨论uni和SMP内核中自旋锁的行为时,我们深入研究了代码并发现了一条令我们感到惊讶的行,我们无法弄清楚为什么会这样做。

短暂的电话跟踪,以显示我们来自哪里:

spin_lock calls raw_spin_lock

raw_spin_lock calls _raw_spin_lock

on a uni-processor system, _raw_spin_lock is #defined as __LOCK

__ LOCK是一个定义:

#define __LOCK(lock) \
  do { preempt_disable(); ___LOCK(lock); } while (0)

到目前为止,这么好。我们通过增加内核任务的锁定计数器来禁用抢占。我假设这样做是为了提高性能:因为你不应该在很短的时间内持有一个自旋锁,你应该只是完成你的关键部分而不是被打断,并且可能有另一个任务在等待你时将调度切片旋转掉光洁度。

然而,现在我们终于回答了我的问题。相应的解锁代码如下所示:

#define __UNLOCK(lock) \
  do { preempt_enable(); ___UNLOCK(lock); } while (0)

为什么你会在___UNLOCK之前调用preempt_enable()?这对我们来说似乎非常不直观,因为你可能在调用preempt_enable之后立即被抢占,而没有机会释放你的自旋锁。感觉这会使整个preempt_disable / preempt_enable逻辑有些无效,特别是因为preempt_disable在其调用期间专门检查锁定计数器是否再次为0,然后调用调度程序。在我们看来,首先释放锁定会更有意义,然后减少锁定计数器,从而可能再次启用调度。

我们缺少什么?在___UNLOCK之前调用preempt_enable而不是反过来的想法是什么?

1 个答案:

答案 0 :(得分:3)

您正在查看单处理器定义。正如spinlock_api_up.h中的评论所说(http://lxr.free-electrons.com/source/include/linux/spinlock_api_up.h#L21):

/*
 * In the UP-nondebug case there's no real locking going on, so the
 * only thing we have to do is to keep the preempt counts and irq
 * flags straight, to suppress compiler warnings of unused lock
 * variables, and to add the proper checker annotations:
 */

___LOCK___UNLOCK宏用于注释目的,除非定义__CHECKER__(由sparse定义),否则最终会被编译出来

换句话说,preempt_enable()preempt_disable()是在单个处理器案例中进行锁定的人。