浅谈linux的死锁检测-alex-huang-ChinaUnix博客
死锁:就是多个进程(≥2)因为争夺资源而相互等待的一种现象,若无外力推动,将无法继续运行下去。注意,只有在多进程或者多线程之间或者他们与中断之间相互通讯或者共享资源才有可能发生死锁,单线程或者进程之间没有联系的话,一般不会发生死锁。锁的种类比较多,这里主要说自旋锁和信号量。两者的差别就在于前者获得不到资源时的动作是不断的资源(即忙转浪费cpu的cycles)而后者则表现为睡眠等待。
死锁的基本情况如下:
(一)、自旋锁
1 递归使用:同一个进程或线程中,申请自旋锁,但没有释放之前又再次申请,一定产生死锁。
2 进程得到自旋锁后阻塞,睡眠:在获得自旋锁之后调用copy_from_user()、copy_to_ser()、和kmalloc()等有可能引起阻塞的函数。
3 中断中没有关中断,或着因为申请未释放的自旋锁:在中断中使用自旋锁是可以的,应该在进入中断的时候关闭中断,不然中断再次进入的时候,中断处理函数会自旋等待自旋锁可以再次使用。或者在进程中申请了自旋锁,释放前进入中断处理函数,中断处理函数又申请同样的自旋锁,这将导致死锁。
4 中断与中断下半部共享资源和中断与进程共享资源死锁出现的情况类似。
5 中断下半部与进程共享资源和中断与进程共享资源死锁出现的情况类似。
自旋锁三种状态:
自旋锁保持期间是抢占失效的(内核不允许被抢占)。
1 单CPU且内核不可抢占:
自旋锁的所有操作都是空。不会引起死锁,内核进程间不存在并发操作进程,进程与中断仍然可能共享数据,存在并发操作,此时内核自旋锁已经失去效果。
2 单CPU且内核可抢占:
当获得自旋锁的时候,禁止内核抢占直到释放锁为止。此时可能存在死锁的情况是参考自旋锁可能死锁的一般情况。
禁止内核抢占并不代表不会进行内核调度,如果在获得自旋锁后阻塞或者主动调度,内核会调度其他进程运行,被调度的内核进程返回用户空间时,会进行用户抢占,此时调用的进程再次申请上次未释放的自旋锁时,会一直自旋。但是内核被禁止抢占,从而造成死锁。
内核被禁止抢占,但此时中断并没被禁止,内核进程可能因为中断申请自旋锁而死锁。
3 多CPU且内核可抢占:
这才是是真正的SMP的情况。当获得自旋锁的时候,禁止内核抢占直到释放锁为止。
(二)信号量
1 递归使用: 同理,在同一个进程或线程中,申请了信号量,但没有释放之前又再次申请,进程会一直睡眠,这种情况一定死锁。
2 进程得到信号量后阻塞,睡眠:由于获取到信号量的进程阻塞或者随眠,其他在获取不到后信号量也会进入睡眠等待,这种情况可能造成死锁。
3 中断中申请信号量:由于信号量在获取不到自旋锁后会进入睡眠等待,中断处理函数不允许睡眠,如果睡眠,中断将无法返回。
4 中断下半部申请信号量:中断下半部允许睡眠,这种情况不会造成死锁。
5 俩个进程相互等待资源:进程1获得信号量A,需要信号量B,在进程1需要信号量B之前进程2获得信号量B,需要信号量A。进程1、2因相互等待资源而死锁。
上面是死锁的基本情况和类型。linux本身就提供了检测死锁的机制,如下:
(一)D状态死锁检测
所谓D状态死锁:进程长时间(系统默认配置120秒)处于TASK_UNINTERRUPTIBLE 睡眠状态,这种状态下进程不响应异步信号。如:进程与外设硬件的交互(如read),通常使用这种状态来保证进程与设备的交互过程不被打断,否则设备可能处于不可控的状态。
对于这种死锁的检测linux提供的是hungtask机制,主要内容集中在Hung_task.c文件中。具体实现原理如下:
1)、系统创建normal级别的khungtaskd内核线程,内核线程每120秒检查一次,检查的内容:遍历所有的线程链表,发现D状态的任务,就判断自最近一次切换以来是否还有切换发生,若是有,则返回。若没有发生切换,则把任务的所有调用栈等信息打印出来。
2)、具体实现过程如下:
Read full article from 浅谈linux的死锁检测-alex-huang-ChinaUnix博客
No comments:
Post a Comment