当我和一些同事讨论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而不是反过来的想法是什么?
答案 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()
是在单个处理器案例中进行锁定的人。