huyc

导航

Linux避免死锁的一些机制

死锁应该有3个原因:

1.独占资源

2.循环等待

3.不可剥夺

网上看到的4个条件,实际上还是3个,因为请求和等待导致的死锁毕竟有点让人难堪,实际上的死锁多是因为请求等待导致的循环等待。

在用户态,由于Linux提倡进程之间的公平,线程之间甚至没有优先级之分,每个进程/线程都会有机会执行,所以不会出现那种高优先级进程/线程抢占低优先级之后,由于低优先级进程/线程的等待导致的死锁,所以用户态还不需要关心这样的问题。

用户态遇到的死锁主要是循环等待,退几步来说,可能就是在临界区sleep的死锁了,查这种死锁很简单,为锁添加计数值就可以。

此外,之前提到pthread_mutex_t是具有优先级的,一个优先级比锁的优先级高的线程无法对其加锁,这防止了高优先级线程抢占低优先级线程的资源而导致饥饿,优先级导致的饥饿问题到内核态就变得非常麻烦。

还有一个就是自死锁,重复对互斥量加锁两次,线程自己等待自己,还是属于循环等待类型的。

在内核态,死锁问题除了在用户态的那些问题,最麻烦的就是存在不同的优先级与并发。

首先要明确,内核态的并发有以下几个因素:

1.中断并发,任何时候都可能发生,也是内核抢占的基础之一,通过关中断来禁止并发

2.多处理器并发,具备多个处理器核心时会发生,是真正的并发,通过加锁来禁止并发

3.内核抢占并发,在支持内核抢占时会发生,属于轮换并发,通过设置标识来禁止并发

4.调度并发,在进程上下文的切换时发生,内核共享处理器时间的基础,不能禁止,但内核保证所有进程/线程都将得到执行,这跟用户态的问题一致

中断在系统中优先级最高,任何一个时间点上都可能发生,由于时序需要,它所执行的代码都必须极度简单,通过用户态那样的优先级策略来使加锁失败显然不好,中断上下文所面临的主要问题有3个,第一是SMP系统的处理器并发,此时必须对共享数据加锁;第二是它所打断的代码的并发,如果打断的代码已经加锁,此时必是死锁无疑,故而所有内核代码,只要跟中断处理程序共享锁,必须先关中断,防止死锁;第三是中断处理程序之间的并发,中断可以打断另外一个中断,如果中断处理程序之间共享锁,这如果发生在同一个处理器上是会死锁的,发生在不同处理器上则不会。总而言之,只要跟中断处理程序共享的任何锁都要关中断,中断的死锁主要就是抢占引起的。

处理器并发的主要问题是为了优化效率,采用了一些处理器私有的数据,不必要加锁,对这些数据的访问必须禁止内核抢占,但不必要禁止中断。禁止抢占的问题在于抢占的代码会重新调度处理器,会访问到错误的数据,这当然不是本文重点,重点是死锁,多枚处理器如果出现互相抢占资源导致的循环等待简直就是奇观,CPU的核心之间相互循环等待,这有点太坑了。CPU并发使用多个资源应该按照一致的顺序持有,出于优雅美观的需求,按顺序释放也是非常必要的。处理器的死锁主要是循环等待引起的。

从内核抢占的名字可以看出来,它的死锁问题也主要是抢占引起的,但它不像中断那样优越,它必须满足一堆的条件才能抢占,所以规避了很多的问题,实际需要注意的主要就剩下调度所产生的轮换并发了,这里有一篇不错的博文可以看看,论述了内核抢占的机制。

调度并发实际上就是轮换使用处理器的结果,这导致任务的碎片化,它是进程抽象的基础,它的代码按逻辑执行,但任何一点都可能被抢占,所以它需要注意的就是,注意不要去惹中断和处理器的私有数据,不得已要去弄一下了,记得关中断,加锁,禁止抢占啥的就OK了。

 

posted on 2012-12-18 17:24  huyc  阅读(6123)  评论(0编辑  收藏  举报