内核自旋锁在释放锁之前启用抢占
作者:互联网
当我与一些同事讨论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,locking,kernel,linux-kernel,linux 来源: https://codeday.me/bug/20191120/2043484.html