死锁的原因很多,其中一种比较常见的是 线程占用临界区后被强杀,导致临界区没有释放,因此当其它线程访问该临界区就会死等。
可是到底是谁强杀了线程了呢?被强杀的线程当时的调用线又是什么呢?
对于问题1:有两种办法找到答案。
一)搜索所有的代码,查看是否有调用terminatethread的地方。
二)在内存中搜索调用terminatethread的指令。
方法一有一定难度,因为开发人员可能没有所有代码的权限。
相比方法一,方法二就更加的直接了当了,在程序的内存中直接搜索call terminatethread的指令,找到对应的模块及调用函数,然后再与模块相关的开发人员确认即可。
现在以我所在的项目的一个死锁例子来看一下,如何在内存中搜索调用terminatethread的指令。
第一步,找到terminatethread函数地址。
0:000> x *!*_imp__TerminateThread*
0023b6f4 VFToolkit0101D!_imp__TerminateThread = <no type information>
00d6f63c RTDAcc!_imp__TerminateThread = <no type information>
0102f070 VFToolkit0101!_imp__TerminateThread = <no type information>
012ec020 SCUtility_VC6D!_imp__TerminateThread = <no type information>
013e3024 SCSessionUtil_VC6D!_imp__TerminateThread = <no type information>
0e7a7014 PostTripQuery_VC6D!_imp__TerminateThread = <no type information>
0ea75010 PostTripClient!_imp__TerminateThread = <no type information>
第二步:找到调用_imp__TerminateThread 的函数指令地址
0:000> s -b 0 L?80000000 ff 15 10 50 a7 0e
0ea00f84 ff 15 10 50 a7 0e 3b f4-e8 3f 00 03 00 8b f4 8b ...P..;..?......
其中 ff 15 为call 指令二进制码.
第三步:确定第三步找到的指令地址对应的函数名
0:000> uf 0ea00f84
PostTripClient!CPostTripClientDetailWnd::BeginWork+0xb4 [E:\Daily_Build\code\src\Set_Nuclear\Tables\PostTripClient\PostTripClientDetailWnd.cpp @ 912]:
912 0ea00f84 ff151050a70e call dword ptr [PostTripClient!_imp__TerminateThread (0ea75010)]
912 0ea00f8a 3bf4 cmp esi,esp
912 0ea00f8c e83f000300 call PostTripClient!chkesp (0ea30fd0)
914 0ea00f91 8bf4 mov esi,esp
这时我们就知道PostTripClientDetailWnd.cpp 的912 行
CPostTripClientDetailWnd::BeginWork调用了TerminateThread 函数,在再相应的开发人员确认,该处TerminateThread 是否有可能造成死锁问题。
对于问题二:被强杀的线程当时的调用线又是什么呢?Win7操作系统在线程被强杀后会清除调用栈数据,所以当时的调用栈就无从得知了,XP操作系统则可通过我的另一篇文章《Windbg调试技术之寻找消失的线程.doc》