死循环/卡锁之代码段定位
- pstack 进程号
pstack是通过采集某个时间点,当前进程调用函数的情况,通过多次调用pstack,发现某个函数一直出现,那么,绝大多数情况下,该函数出现(死循环or死锁)。假设func函数出现的概率很高。
假设就是出现了死锁、死循环,那么这两种情况有所不同。
(1)死锁:pstack显示卡死的代码行永远是某一行,且该行代码卡在锁处!(但是这种情况并不一定都是死锁,也有可能是死循环导致的),比如:
(2)死循环:pstack显示卡死的代码一直在某个函数范围内,但是行号会在函数内有所变化void timer_proc() { // 定时器 LOCK(A); { while(1) { } // 死循环,拿到A锁 } UNLOCK(A); } void process() { LOCK(A); // 此时,想拿A锁,但是A锁被timer_proc函数拿到,且由于死循环,A锁不能释放,出现的现象是代码卡在该行! UNLOCK(A); } 这种情况,pstack查看的现象: 1. timer_proc()函数会一直存在,且代码卡在 while(1) {} 循环代码段附近 2. 会一直卡在process()函数的LOCK(A)代码行 误解:由于一直卡在LOCK(A)锁,第一印象会误以为发生了卡锁 本质上:该现象却是由于死循环导致的。
- top命令、死循环/死锁
如果某进程出现死循环,那么使用top查看cpu使用率时,将发现该进程cpu占用率会很高,达到100。 - gdb调试进程,分析死循环(断点区间:由大变小)
1-首先,将断点打到函数(大区间),然后C,发现C过很久也一直卡主,说明程序可能在该函数内的某个代码段死循环了,导致没法重新进来
2-然后,将断点打到pstack显示的行号处(小区间),执行C,如果是死循环,C将会立刻退出,多次C,会一直退出(退出说明程序一直经过该行号)
3-最后,如果出现上述情况,那绝大多数是死循环了!此时应该用gdb打印各个变量的值,分析代码逻辑,证明的确是死循环。
卡锁原因
上面说的很清楚,死锁的现象是pstack一直卡在某一行,此时,查看代码行,如果发现该代码行的确是LOCK,那么99%是死在这个锁上!(程序死在某个锁上的原因分析)
1-锁A被一个进程持有,且进程没有解锁;导致其他进程想加锁A,就会被卡住
进程没解锁的原因:
1- LOCK(); 死循环; UNLOCK()
2- LOCK(); 没UNLOCK()
2-AB锁:需要分析代码,查看是否发生AB、BA加锁顺序
问题解决之gdb调试
查看所有线程的堆栈:thread apply all bt
切换到某个线程:thread 线程号