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

当我与一些同事讨论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的想法是什么?

解决方法:

您正在查看单处理器定义.正如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()是在单个处理器中进行锁定的对象.

上一篇:scheduling定时任务手记


下一篇:Work Scheduling URAL - 1099 一般图最大匹配带花树