于 2010年10月19日 11:23, 胡杨树 写道:
> 使用spinlock时,需要包含include/linux/spinlock.h, 这是一个顶层文件
>
> #define spin_lock(lock) _spin_lock(lock)
> _spin_lock的SMP版本在kernel/spinlock.c中实现,UP版本的include/linux/spinlock_up.h中实现
>
>
> 不管是SMP/UP,在调用spin_lock的时候为何要禁止内核抢占?
为了让本处理器上的lock持有者可以有机会释放锁呗~
避免优先级反转。
我认为目的是为了不被调度出去。因为spin lock的设计初衷就是在段时间内进行轻量级加锁,
锁的争用者进行循环等待,而不调度出去,这样提高性能。
于 2010年10月19日 14:44, Miao Xie 写道:
> On Tue, 19 Oct 2010 11:23:38 +0800, 胡杨树 wrote:
>> 使用spinlock时,需要包含include/linux/spinlock.h, 这是一个顶层文件
>>
>> #define spin_lock(lock) _spin_lock(lock)
>> _spin_lock的SMP版本在kernel/spinlock.c中实现,UP版本的include/linux
>> /spinlock_up.h中实现
>>
>>
>> 不管是SMP/UP,在调用spin_lock的时候为何要禁止内核抢占?
>
> 我认为目的是为了不被调度出去。因为spin lock的设计初衷就是在段时间内进行
> 轻量级加锁,
> 锁的争用者进行循环等待,而不调度出去,这样提高性能。
+1
呵呵,刚才我看错了,以为是抢spinlock时~
--
Yinglin Luan
Best Regards
所以,恰恰是这个自旋锁才会导致优先级翻转,rt-linux才要求有可以睡眠的自旋锁实现。
--
Regards,
Changli Gao(xia...@gmail.com)
+1
我觉得可以从两方面考虑这个问题:
1. spin_lock()本身不能睡眠,要么直接进去,要么处于忙等状态。
这就是Yinglin所言。
2. 持有spin_lock之后,必须保证不让一个以上的进程同时进入critical section,
让进去的进程一次执行完毕,让进不去的进程在spin。所以才会禁止抢占,否则
另外一个进程也有可能进去。在SMP上除了本CPU禁止抢占以外,还需要让
别的CPU上的进程进行spin,同时允许抢占。
--
Live like a child, think like the god.
那样spin_lock保护的代码的原子性就得不到保证了。;-)
spin_lock(); //assume no preempt_disable
statement1;
<============ preempt
statement2;
spin_unlock();
虽然别的进程不能执行statement1和statement2。
这个理解不对。spin_lock_irqsave()是为了防止和IRQ call path发生竞争,比如:
spin_lock();
statement1;
<====== interrupt!!
spin_lock(); <====== BUG
statement2;
spin_unlock();
spin_lock_bh()同理,只不过是softirq。
我认为胡的说法是对的,如果进程只有spinlock而没有关中断,难免会被cpu的IRQ所中断,耽搁过长时间。而你所举的例子只是发生在irq
handler或者是softirq中也有同一个的spinlock的情形。当然,进程之间共享变量的保护,spinlock就足够,没必要非得禁止中断。
--
Regards,
Changli Gao(xia...@gmail.com)
如果必须关中断才能保证的话,那spin_lock()里就应该关了,
而不会再提供一个spin_lock_irqsave()接口,这个接口存在的目的就是
为了防止前面说的竞争。
所以,spin_lock()保证它保护的代码不被其它进程中断就够了,即我前面
提到的不涉及中断的原子性。
--
Live like a child, think like the god.
啊!其实spin_lock()没必要保证里面的代码不被其它进程中断,因为任何
其它地方读写spin_lock()里代码操作的数据的话也都应该加spin_lock()。
那这么看来spin_lock()在UP上禁止抢占只是为了防止Priority Inversion Problem了。
:-/
也不能这么说,前面有人已经分析过了,禁止抢占会导致高优先级进程得不到及时调度,降低了系统的实时性。而优先级反转应该也不会出现,因为spin_unlock的时候,会调度最高优先级程序运行。
spinlock在不可抢占UP系统上展开为空,你试想一下,如果在可抢占UP系统上,也展开为空,而不是禁止抢占,它还能起到保护共享数据的作用么?
--
Regards,
Changli Gao(xia...@gmail.com)
不是很懂
On Wed, 2010-10-20 at 00:35 +0800, Américo Wang wrote:
> spin_lock_irqsave()
--
Liu Jianing <spong...@gmail.com>
Key Laboratory of Computer System and Architecture, CAS
我有个问题,考虑这种情况:
对同一CPU:
spin_lock()
....
(内核抢占,切换到另一个进程)
你在说使用spin_lock的缺点?我在说spin_lock的实现,你跑题了。
至于你说的问题,它的实现就决定了如此。;-) 所以你还有其它很多选择,
mutex,semaphore,rwlock,rwsem,RCU。
>
>spinlock在不可抢占UP系统上展开为空,你试想一下,如果在可抢占UP系统上,也展开为空,而不是禁止抢占,它还能起到保护共享数据的作用么?
>
可以的。如果不考虑 priority inversion,我们完全可以:
for(;;) {
if (is_not_locked()){
lock_it();
break;
} else
spin();
}
这样也可以保护。但是Priority Inversion导致的死锁就会发生。
实际上,不看抢占的话,上面的伪代码正是spin_lock在SMP上的实现。
在SMP上如果其它CPU在spin的话它还要打开抢占,就是为了降低你说的实时性。
所以就成了:
for(::) {
preempt_disable();
if (try_lock())
break;
preempt_enable();
check_and_spin();
后果就是前面几位提到的priority inversion。
假设有进程A,B,C;优先级A>B>C;A,C进程都需要临界区数据;而B不需要;
如果某个条件下,A进程得到机会执行,但C却持有它需要的锁,所以A虽然得到CPU
但是却只能等待;CPU上又回放回C;接着B试图执行,它可以将C挤出去,因为它的
优先级高于C,而且它也不需要C持有的锁;B执行结束后,C又回到CPU上继续执行
,直到它结束并且放开锁,A才有可能得到锁以便完成任务。那么最终结果就是,B
最先完成,接着是C,最后是A。这个结果就与他们原来定义的优先级不同了,优先
级最高的任务反而最后完成。
如果这里禁止抢占,那么调度就不需要考虑优先级,仅仅依赖于是否可以获得锁。
当C持有锁的时候,A会等待;而B不会被调度进来,因为优先级判断已经失效了。
只能等C执行结束,才有机会调度A或者B。这看起来就是spin_lock想要的效果。但
是这种情况里,就没有什么优先级的概念了,系统的实时性——也就是按照优先级调
度的能力——就无法得到保障了。
/大头阿当
Priority inversion并非导致死锁,而是导致高优先级任务反而比低优先级任务完
成的更晚,也就是发生了反转。如果发生死锁的话,“保护临界区”这个任务也就没
有什么意义了。但事实情况是,虽然优先级翻转了,共享数据的访问却没有被破坏
,所以,正如你说的“这样也可以保护”。这句话只对了一半,right? :)
> 实际上,不看抢占的话,上面的伪代码正是spin_lock在SMP上的实现。在SMP上
> 如果其它CPU在spin的话它还要打开抢占,就是为了降低你说的实时性。
打开抢占却降低了实时性?何解?
/阿当
抛除死锁不死锁不谈(会在另一封邮件中回复),这正是我的观点,Changli认为
不禁止抢占就无法保护数据,我认为可以。
>,所以,正如你说的“这样也可以保护”。这句话只对了一半,right? :)
>
那一句后面的“但是”很不明显嘛?:)
>> 实际上,不看抢占的话,上面的伪代码正是spin_lock在SMP上的实现。在SMP上
>> 如果其它CPU在spin的话它还要打开抢占,就是为了降低你说的实时性。
>
>打开抢占却降低了实时性?何解?
漏了“防止”一词。。。
你忘了spin_lock里是忙等而非等待了,所以A不会让出CPU。
所以情况就是A占有CPU却在执行无用的忙等操作,得不锁,持有锁的进程C
因为优先级低无法去释放锁,“死锁”就形成了,只要调度器没有打破这种局面。
如果是支持内核抢占的UP,在spinlock锁住的临界区如果不关闭抢占,那么spinlock就应该展开为空,恰巧在临界区中有被抢占,并且抢占进程也要访问前面的spinlock保护的数据,不就发生竞态了。
--
Regards,
Changli Gao(xia...@gmail.com)
请问展开都为空了,如何去锁住临界区呢?
spin_lock之所以在非抢占的UP上展开为空,是因为根本就没有进程进行竞争,
所以不需要锁。
...对。
> 所以情况就是A占有CPU却在执行无用的忙等操作,得不锁,持有锁的进程C
> 因为优先级低无法去释放锁,“死锁”就形成了,只要调度器没有打破这种局面。
也就是说,假设系统中只有这三个进程,或者A就是系统中最高优先级,那么这个
时候事实上就只有A能在这里做无用功了。其结论就是,如果没有preempt_disable
,那么,spin_lock干脆会导致死锁,就不要谈保护临界区了。
/阿当
所以,如果不是spin_lock而是semaphore或者mutex,这里会发生优先级翻转。
spin_lock的情况特殊,根本没有优先级翻转什么事情。
/阿当
咱么不是讨论为什么抢占UP上的spinlock要禁止抢占么。我的假设是如果不禁止抢占,spinlock不就要和非抢占UP一样为空了。可是展开为空是不行的,因为有其它进程参与竞争啊。
--
Regards,
Changli Gao(xia...@gmail.com)
看你到底把不把这种情况叫做优先级翻转了。
我觉得没任何问题,持有锁的就是应该优先执行,没有锁的进程优先级就应该低,
翻转了。;-)
我觉得你理解的是对的。合理的执行顺序应该是C A B。
>
> 如果这里禁止抢占,那么调度就不需要考虑优先级,仅仅依赖于是否可以获得锁。
> 当C持有锁的时候,A会等待;而B不会被调度进来,因为优先级判断已经失效了。
> 只能等C执行结束,才有机会调度A或者B。这看起来就是spin_lock想要的效果。但
> 是这种情况里,就没有什么优先级的概念了,系统的实时性――也就是按照优先级调
> 度的能力――就无法得到保障了。
>
禁止抢占正是防止优先级反转的一个方法:优先级天花板(http://baike.baidu.com/view/2422471.html?fromTaglist)。spin
unlock的时候会选择最高优先级进程执行,所以上例的执行顺序应该是C A B.
--
Regards,
Changli Gao(xia...@gmail.com)
你理解错了,我们讨论的不是在现有的linux内核实现上去掉禁止抢占,
而是我们为什么一定要UP禁止抢占。
不是一会事么?我只是从反面论证,如果不禁止抢占会怎么样,以此来证明,禁止抢占是必须的。我逻辑有问题么? :)
--
Regards,
Changli Gao(xia...@gmail.com)
有的,请参考我之前一封邮件中的伪代码。;)
没有问题,反正我理解了,^_^。
其实两位说的是一个硬币的两面,是一回事。结论是,spin_lock的时候调用
preempt_disable是必须的(内核代码会处理不同情况的抢占)。
最后一个疑问——这个是我读代码不熟悉造成,见谅,Wang和你都提到现有实现会将
持有spinlock的那个进程设置为最高优先级,也就是采用priority ceiling的做法
来避免发生“优先级翻转”或者“忙等”,能否指出具体相关的代码,我想看看。谢谢
。
/阿当
你不认为禁止抢占就等同于把该进程以最高的优先级运行嘛?;)
禁止了抢占也就锁定了调度,相当于设定当前进程为最高优先级。不知道mutex当前是不是有优先级继承的特性,你可以看看。
Documentation/rt-mutex*.txt
从逻辑上说,是这样,因为禁止后就只有唯一一个优先级。
两位提到这个的时候,我以为是将上例中的C设置为系统中的最高优先级,而依然
保持其他进程优先级不变的做法。
/阿当
你这这个说法是正确的。反过来说,如果在支持抢占的UP上将spinlock展开为空的
话,临界区就得不到保护。如果把前面的例子拿到这种情况下,进程C会直接被A挤
出去,同时也失去对临界区的保护。right?
但是Gao这个说法也是对的,与你的意思一样?逻辑似乎没错吧。如果有错,请明
确地讲一讲。
/阿当
At 2010-10-20 13:11:12,"Adam Jiang" <jiang...@gmail.com> wrote: >On Wed, Oct 20, 2010 at 01:07:34PM +0800, Américo Wang wrote: >> On Wed, Oct 20, 2010 at 01:33:30PM +0900, Adam Jiang wrote: >> >On Wed, Oct 20, 2010 at 10:30:36AM +0800, ZHANG XIAO-MING-MND637 wrote: >> >> ZHANG XIAO-MING-MND637 wrote: >> >> > Américo Wang wrote: >> >> >> 2010/10/20 Américo Wang <xiyou.w...@gmail.com>: >> >> >>> >> >> >>> ?,spin_lock()保证它保护的代码不被其它进程中断就够了,即我前面 >> >> >>> 提到的不涉及中断的原子?? >> >> >> >> >> >> 啊!其实spin_lock()没必要保证里面的代码不被其它进程中断,因为任? >> >> >> 其它地方读写spin_lock()里代码操作的数据的话也都应该加spin_lock()? >> >> >> >> >> >> 那这么看来spin_lock()在UP上禁止抢占只是为了防止Priority Inversion Problem了? >> >> >> :-/ >> >> >> _____ >> >> Sorry I get a problem in my Thunderbird's character encoding. >> >> Try GB2312: >> >> >> >> 我有个问题,考虑这种情况: >> >> >> >> 对同一CPU: >> >> spin_lock() >> >> .... >> >> (内核抢占,切换到另一个进程) >> >> spin_lock() >> >> .... >> >> >> >> 如果不perrmpt_disable(), 那么会有这种情况发生吗?如果有那后果是什么? >> > >> >后果就是前面几位提到的priority inversion。 >> > >> >假设有进程A,B,C;优先级A>B>C;A,C进程都需要临界区数据;而B不需要; >> > >> >如果某个条件下,A进程得到机会执行,但C却持有它需要的锁,所以A虽然得到CPU >> >但是却只能等待;CPU上又回放回C;接着B试图执行,它可以将C挤出去,因为它的 >> >> 你忘了spin_lock里是忙等而非等待了,所以A不会让出CPU。 > >...对。 > >> 所以情况就是A占有CPU却在执行无用的忙等操作,得不锁,持有锁的进程C >> 因为优先级低无法去释放锁,“死锁”就形成了,只要调度器没有打破这种局面。 > >也就是说,假设系统中只有这三个进程,或者A就是系统中最高优先级,那么这个 >时候事实上就只有A能在这里做无用功了。其结论就是,如果没有preempt_disable >,那么,spin_lock干脆会导致死锁,就不要谈保护临界区了。 > >/阿当 >_______________________________________________ >Linux 内核开发中文邮件列表 >Linux-...@zh-kernel.org >http://zh-kernel.org/mailman/listinfo/linux-kernel >Linux 内核开发中文社区: http://zh-kernel.org
我认为这种情况严格的说不能叫死锁,而是饥饿现象。如果调度算法只满足高优先级的进程,低优先级进程会一直处于饥饿状态。但是Linux内核的调度机制能适当的照顾低优先级进程,使其有机会执行。
所以Preempt_disable在Linux系统中不是必须的(除非存在RealTime进程,这样获得SpinLock并且被抢占的低优先级进程就会一直处于饥饿状态)。Spinlock适用的场合是短时间处于临界区的情况,如果没有preempt disable,低优先级进程会频繁被高优先级进程抢占,这样反而使系统效率降低,违背Spinlock的设计初衷。