这个经典的同步问题,有很多文章讲过,写这篇博客,权当是疏理一下相关的技术概念和分析方法。
问题的现象:
如果在一个DLL的DllMain()函数中创建一个线程,那么当该DLL被引导时,就会导致主(host)进程陷入死锁。
问题重现方法:
用VS生成一个DLL工程,在DllMain()函数里创建一个线程,并等待该进程结束。简单起见,子线程函数不必包含任何工作逻辑,直接返回即可。
DLL工程源代码:
#include <windows.h>
#include <stdio.h>
WINAPI InitDllProc LPVOID lpParam )
{
return 1;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
BOOL bRet=FALSE;
switch(fdwReason)
{
case DLL_PROCESS_ATTACH:
{
DWORD dwId=0;
HANDLE hThread=NULL;
hThread = CreateThread(NULL, 0, InitDllProc, NULL, 0, &dwId);
if(hThread)
{
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread);
bRet=TRUE;
}
}
break;
}
return bRet;
}
另生成一个普通的console程序工程,在main()函数里调用LoadLibrary()引导DLL工程生成的DLL。
console程序源代码。
#include <windows.h>
#include <stdio.h>
int _tmain(int argc, _TCHAR* argv[])
{
_tprintf(L"Load library ...\n");
LoadLibrary(L"DllMain.dll");
_tprintf(L"Load library done.\n");
return 0;
}
把创建的DllMain.dll和LoadDll.exe放在同一个目录下,并双击运行LoadDll.exe。可以看到主进程输出"Load library ..."之后就再也没有动作。
用任务管理器看到该进程的CPU使用率一直保持0不变。
据此可以判断,LoadDll.exe进程陷入了某种死锁状态。
问题调试
调试环境:
OS: Windows 7 SP1 Build 7601 (X86)
WinDbg 6.3.9600.16384 X86
在LoadDll.exe限入死锁状态后,用WinDbg attach到该进程。用!cs -l命令查看锁的状态。
-----------------------------------------
DebugInfo = 0x779f7540
Critical section = 0x779f7340 (ntdll!LdrpLoaderLock+0x0)
LOCKED
LockCount = 0x1
WaiterWoken = No
OwningThread = 0x00000fa0
RecursionCount = 0x1
LockSemaphore = 0x30
SpinCount = 0x00000000
可以看到有一个名为ntdll!LdrpLoaderlock的关键区(Critical Section)正处于锁状态,对应的句柄号是0x30,拥有这个关键区的线程是fa0。