DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析
? ? ? ? 不知道大家是否思考過(guò)一個(gè)過(guò)程:系統(tǒng)試圖運(yùn)行我們寫(xiě)的程序,它是怎么知道程序起始位置的?很多同學(xué)想到,我們?cè)诰帉?xiě)程序時(shí)有個(gè)函數(shù),類似Main這樣的名字。是的!這就是系統(tǒng)給我們提供的控制程序最開(kāi)始的地方(注意這兒是提供給我們的,而實(shí)際有比這個(gè)還要靠前的main)。于是看到DllMain就可以想到它是干嘛的了:Dll的入口點(diǎn)函數(shù)。那何時(shí)調(diào)用這個(gè)函數(shù)的呢?以及各種調(diào)用場(chǎng)景都傳給了它什么參數(shù)呢?(轉(zhuǎn)載請(qǐng)指明出于breaksoftware的csdn博客)
? ? ? ? 進(jìn)程對(duì)DLL的載入卸載,以及新線程的創(chuàng)建和退出都會(huì)導(dǎo)致對(duì)DllMain的調(diào)用。于是,我們?cè)O(shè)計(jì)了如下流程
? ? ? ? 為了盡可能排除一些因素對(duì)我們實(shí)驗(yàn)的影響,所有線程函數(shù)公用一個(gè)簡(jiǎn)單的例程函數(shù)
static DWORD WINAPI ThreadRoutine(LPVOID lpParam) {DWORD dwTID = GetCurrentThreadId();PrintLog("Thread%s %u\n", (LPSTR)lpParam, dwTID );Sleep(15000);PrintLog("\nThread%s Will Exit\n", (LPSTR)lpParam );return 0;
}
? ? ? ? DllMain函數(shù)也是非常簡(jiǎn)單,兩個(gè)DLL的DllMain函數(shù)99.99%是相同的,只是在最后輸出所在DLL時(shí)列出了各自的DLL名字,以Dll1為例
BOOL APIENTRY DllMain( HMODULE hModule,DWORD ul_reason_for_call,LPVOID lpReserved)
{string strReason;DWORD TID = GetCurrentThreadId();switch (ul_reason_for_call) {case DLL_PROCESS_ATTACH:{strReason = "DLL_PROCESS_ATTACH";}break;case DLL_PROCESS_DETACH:{strReason = "DLL_PROCESS_DETACH";}break;case DLL_THREAD_ATTACH:{strReason = "DLL_THREAD_ATTACH";}break;case DLL_THREAD_DETACH:{strReason = "DLL_THREAD_DETACH";}break;default:{strReason = "default";}break;}PrintLog("Dll1 TID:%u %s\n", TID, strReason.c_str() );return TRUE;
}
? ? ? ? 現(xiàn)在我們說(shuō)下我設(shè)計(jì)這個(gè)流程的考慮:
? ? ? ? ?0 1 ?這個(gè)過(guò)程是為了查看Dll加載后,DllMain被調(diào)用是否受之前創(chuàng)建的線程影響。如果受到影響,我們應(yīng)該能看到Dll1中輸出的信息中包含有線程A TID的記錄。反之則沒(méi)有記錄。?
? ? ? ? 2 這個(gè)過(guò)程是為了驗(yàn)證創(chuàng)建新線程,對(duì)之前加載的Dll的DllMain調(diào)用情況。如果Dll1的DllMain輸出了線程B TID記錄,那么說(shuō)明新線程創(chuàng)建會(huì)讓之前加載Dll的DllMain。反之說(shuō)明創(chuàng)建新線程不會(huì)調(diào)用之前加載DLL的DllMain。
? ? ? ? 3 是為了再次驗(yàn)證0,1這個(gè)過(guò)程得出的結(jié)論。
? ? ? ? 4 是為了再次驗(yàn)證2這個(gè)過(guò)程得出的結(jié)論。
? ? ? ? 5 創(chuàng)建的線程是為了之后驗(yàn)證線程正常退出和強(qiáng)制關(guān)閉之間的影響。
? ? ? ? 61,62 是為了驗(yàn)證FreeLibrary是否會(huì)對(duì)之前對(duì)此DLL調(diào)用DllMain的線程存在影響。也就是想查看之前在創(chuàng)建線程時(shí)對(duì)Dll調(diào)用DllMain的線程,是否會(huì)發(fā)現(xiàn)要FreeLibrary了,從而對(duì)該Dll再調(diào)用DllMain做某些處理(比如清理)。該過(guò)程導(dǎo)致DllMain中輸出的信息包括那些線程TID的記錄,則說(shuō)明存在影響(其他線程調(diào)用DllMain),否則說(shuō)明不存在影響(其他線程不調(diào)用DllMain)。
? ? ? ? 6 驗(yàn)證通過(guò)強(qiáng)制關(guān)閉線程對(duì)DllMain調(diào)用的影響。
? ? ? ? 7 8 9 驗(yàn)證對(duì)不同DLL的DllMain調(diào)用情況可能存在不同的線程,在退出時(shí),是否會(huì)調(diào)用DllMain,以及它們對(duì)DllMain的調(diào)用規(guī)律。
? ? ? ? 10 101 102 103 104等是通過(guò)不同方式驗(yàn)證進(jìn)程退出對(duì)DllMain是否存在調(diào)用,以及調(diào)用的規(guī)律。
? ? ? ? 我們先在主線程中用 1 2 3 4 5 6 7 8 9 10 這個(gè)流程,其結(jié)果是
| ? | MainTid:1056 | 主線程ID是1056 |
| 0 | CreatThread A ThreadA 3156 | A線程ID是3156 |
| 1 | LoadLibraryA Dll1 Dll1 TID:1056 DLL_PROCESS_ATTACH | Dll1加載了,它是主線程(1056)加載的。調(diào)用原因是DLL_PROCESS_ATTACH。而它的加載,并不會(huì)導(dǎo)致之前創(chuàng)建的A線程對(duì)其調(diào)用DllMain。 |
| 2 | CreatThread B Dll1 TID:4784 DLL_THREAD_ATTACH ThreadB 4784 | B線程(4784)在執(zhí)行到線程函數(shù)之前,會(huì)去調(diào)用之前加載了但還沒(méi)有卸載的Dll1的DllMain函數(shù)。調(diào)用原因是DLL_THREAD_ATTACH,而不是之前的DLL_PROCESS_ATTACH。 |
| 3 | LoadLibraryA Dll2 Dll2 TID:1056 DLL_PROCESS_ATTACH | Dll2加載了,調(diào)用其DllMain是主線程。調(diào)用原因是DLL_PROCESS_ATTACH。加載后,并不會(huì)導(dǎo)致線程A、B去調(diào)用其DllMain。 |
| 4 | CreatThread C Dll1 TID:4052 DLL_THREAD_ATTACH Dll2 TID:4052 DLL_THREAD_ATTACH ThreadC 4052 | C線程(4052)在執(zhí)行其線程函數(shù)之前,會(huì)去調(diào)用之前在主線程中加載了但還沒(méi)有卸載的DLL的DllMain函數(shù),調(diào)用原因是DLL_THREAD_ATTACH。 |
| 5 | CreatThread D Dll1 TID:3440 DLL_THREAD_ATTACH Dll2 TID:3440 DLL_THREAD_ATTACH ThreadD 3440 | 同上。 |
| 6 | TerminateThread D | 強(qiáng)制關(guān)閉線程,不會(huì)導(dǎo)致任何DllMain的調(diào)用。 |
| 7 | ThreadA Will Exit Dll2 TID:3156 DLL_THREAD_DETACH Dll1 TID:3156 DLL_THREAD_DETACH | 線程A退出之前,會(huì)調(diào)用之前加載了但還沒(méi)有卸載的所有DLL的DllMain。注意,此處調(diào)用是線程A(3156),而不是主線程(1056)。調(diào)用原因是DLL_THREAD_DETACH。 |
| 8 | ThreadB Will Exit Dll2 TID:4784 DLL_THREAD_DETACH Dll1 TID:4784 DLL_THREAD_DETACH | 同上。 |
| 9 | ThreadC Will Exit Dll2 TID:4052 DLL_THREAD_DETACH Dll1 TID:4052 DLL_THREAD_DETACH | 同上。 |
| 10 | Proceess Exit Dll2 TID:1056 DLL_PROCESS_DETACH Dll1 TID:1056 DLL_PROCESS_DETACH | 主線程退出前,會(huì)調(diào)用所有加載了但還沒(méi)有卸載的DLL的DllMain。調(diào)用原因是DLL_PROCESS_DETACH。 |
? ? ? ? 為了排除主線程對(duì)我們環(huán)境的影響我們看下在子線程中執(zhí)行以上流程的結(jié)果(之后我們對(duì)流程的修改,都將建立在子線程執(zhí)行流程的基礎(chǔ)之上)
| ? | MainTid:5536 | 執(zhí)行的線程ID是5536 |
| 0 | CreatThread A ThreadA 5684 | A線程ID是5684 |
| 1 | LoadLibraryA Dll1 Dll1 TID:5536 DLL_PROCESS_ATTACH | Dll1加載了,它是執(zhí)行線程(5536)加載的。調(diào)用原因是DLL_PROCESS_ATTACH。而它的加載,并不會(huì)導(dǎo)致之前創(chuàng)建的A線程對(duì)其調(diào)用DllMain。 |
| 2 | CreatThread B Dll1 TID:4716 DLL_THREAD_ATTACH ThreadB 4716 | B線程(4716)在執(zhí)行到線程函數(shù)之前,會(huì)去調(diào)用之前加載了但還沒(méi)有卸載的Dll1的DllMain函數(shù)。調(diào)用原因是DLL_THREAD_ATTACH,而不是之前的DLL_PROCESS_ATTACH。 |
| 3 | LoadLibraryA Dll2 Dll2 TID:5536 DLL_PROCESS_ATTACH | Dll2加載了,調(diào)用其DllMain是執(zhí)行線程(5536)。調(diào)用原因是DLL_PROCESS_ATTACH。加載后,并不會(huì)導(dǎo)致線程A、B去調(diào)用其DllMain。 |
| 4 | CreatThread C Dll1 TID:2620 DLL_THREAD_ATTACH Dll2 TID:2620 DLL_THREAD_ATTACH ThreadC 2620 | C線程(2620)在執(zhí)行其線程函數(shù)之前,會(huì)去調(diào)用之前在執(zhí)行線程中加載了但還沒(méi)有卸載的DLL的DllMain函數(shù),調(diào)用原因是DLL_THREAD_ATTACH。 |
| 5 | CreatThread D Dll1 TID:1016 DLL_THREAD_ATTACH Dll2 TID:1016 DLL_THREAD_ATTACH ThreadD 1016 | 同上。 |
| 6 | TerminateThread D | 強(qiáng)制關(guān)閉線程,不會(huì)導(dǎo)致任何DllMain的調(diào)用。 |
| 7 | ThreadA Will Exit Dll2 TID:5684 DLL_THREAD_DETACH Dll1 TID:5684 DLL_THREAD_DETACH | 線程A退出之前,會(huì)調(diào)用之前加載了但還沒(méi)有卸載的所有DLL的DllMain。注意,此處調(diào)用是線程A(5684),而不是執(zhí)行線程(5536)。調(diào)用原因是DLL_THREAD_DETACH。 |
| 8 | ThreadB Will Exit Dll2 TID:4716 DLL_THREAD_DETACH Dll1 TID:4716 DLL_THREAD_DETACH | 同上。 |
| 9 | ThreadC Will Exit Dll2 TID:2620 DLL_THREAD_DETACH Dll1 TID:2620 DLL_THREAD_DETACH | 同上。 |
| 10 | Dll2 TID:5536 DLL_THREAD_DETACH Dll1 TID:5536 DLL_THREAD_DETACH Proceess Exit Dll2 TID:3904 DLL_PROCESS_DETACH Dll1 TID:3904 DLL_PROCESS_DETACH | 執(zhí)行線程(5536)在退出時(shí)調(diào)用了它加載了但還沒(méi)有卸載的兩個(gè)DLL的DllMain,調(diào)用原因是DLL_THREAD_DETACH。 主線程退出前,會(huì)調(diào)用所有之前加載了但還沒(méi)有卸載的DLL的DllMain。調(diào)用原因是DLL_PROCESS_DETACH。 |
? ? ? ? 看了如此一串后,我想很多人都會(huì)有點(diǎn)暈,現(xiàn)在我總結(jié)一下:
? ? ? ? 一 Dll的加載不會(huì)導(dǎo)致之前創(chuàng)建的線程調(diào)用其DllMain函數(shù)。
? ? ? ? 二 線程創(chuàng)建后會(huì)調(diào)用已經(jīng)加載了的DLL的DllMain,且調(diào)用原因是DLL_THREAD_ATTACH。(DisableThreadLibraryCalls會(huì)導(dǎo)致該過(guò)程不被調(diào)用,之后會(huì)介紹)
? ? ? ? 三 TerminateThread方式終止線程是不會(huì)讓該線程去調(diào)用該進(jìn)程中加載的Dll的DllMain。
? ? ? ? 四 線程正常退出時(shí),會(huì)調(diào)用進(jìn)程中已經(jīng)加載過(guò)的的DLL的DllMain,且調(diào)用原因是DLL_THREAD_DETACH。(不準(zhǔn)確,之后糾正)
? ? ? ? 五 進(jìn)程正常退出時(shí),會(huì)調(diào)用該進(jìn)程中已經(jīng)加載過(guò)的的DLL的DllMain,且調(diào)用原因是DLL_PROCESS_DETACH。(不準(zhǔn)確,之后糾正)
? ? ? ? 六 加載DLL進(jìn)入進(jìn)程空間時(shí)(和哪個(gè)線程LoadLibrary無(wú)關(guān)),加載它的線程會(huì)調(diào)用DllMain,且調(diào)用原因是DLL_PROCESS_ATTACH。
? ? ? ? 我們將過(guò)程6替換為過(guò)程61,并在子線程中執(zhí)行,結(jié)果大部分相似,我把不一樣的地方列出來(lái)(執(zhí)行線程TID是4752)
| 61 | Dll1 TID:4752 DLL_PROCESS_DETACH | 執(zhí)行線程(4752)中卸載了Dll1,則執(zhí)行線程(4752)調(diào)用該DLL的DllMain,且原因是DLL_PROCESS_DETACH。 |
| 6 | TerminateThread D | ? |
| 7 | ThreadA Will Exit Dll2 TID:3688 DLL_THREAD_DETACH | 線程A不會(huì)對(duì)已經(jīng)卸載了的Dll1調(diào)用其DllMain。 |
| 8 | ThreadB Will Exit Dll2 TID:1872 DLL_THREAD_DETACH | 同上。 |
| 9 | ThreadC Will Exit Dll2 TID:5600 DLL_THREAD_DETACH | 同上。 |
| 10 | Dll2 TID:4752 DLL_THREAD_DETACH Proceess Exit Dll2 TID:2364 DLL_PROCESS_DETACH | 同上。 進(jìn)程退出時(shí),對(duì)尚未卸載的DLL調(diào)用其DllMain,且原因是DLL_PROCESS_DETACH。 |
? ? ? ? 基于以上結(jié)果,我們將以上四五兩點(diǎn)結(jié)論再嚴(yán)謹(jǐn)點(diǎn)
? ? ? ? 四 線程正常退出時(shí),會(huì)調(diào)用進(jìn)程中還沒(méi)卸載的DLL的DllMain,且調(diào)用原因是DLL_THREAD_DETACH。
? ? ? ? 五 進(jìn)程正常退出時(shí),會(huì)調(diào)用(不一定是主線程)該進(jìn)程中還沒(méi)卸載的DLL的DllMain,且調(diào)用原因是DLL_PROCESS_DETACH。
? ? ? ? 并得出以下結(jié)論
? ? ? ? 七 DLL從進(jìn)程空間中卸載出去前,會(huì)被卸載其的線程調(diào)用其DllMain,且調(diào)用原因是DLL_PROCESS_DETACH。
? ? ? ? 如果仔細(xì)看過(guò)我試驗(yàn)結(jié)果的同學(xué),應(yīng)該看到一個(gè)現(xiàn)象:線程A不會(huì)對(duì)Dll1調(diào)用DllMain(DLL_THREAD_ATTACH),而在線程A退出時(shí),卻會(huì)調(diào)用DLL1的DllMain(DLL_THREAD_DETACH)。這種不同步的現(xiàn)象是不是讓你內(nèi)心感覺(jué)很疑惑?你說(shuō)windows為什么要這么設(shè)計(jì)呢?我不明白。《windows核心編程》也有對(duì)該現(xiàn)象的一個(gè)描述:雖然當(dāng)系統(tǒng)將該線程連接到該DLL的時(shí)候,不會(huì)向該DLL發(fā)送DLL_THREAD_ATTACH通知。但是當(dāng)系統(tǒng)將該線程與DLL解除連接的時(shí)候,卻會(huì)向該DLL發(fā)送DLL_THREAD_DETACH通知。由于這個(gè)原因,我們?cè)谶M(jìn)行與線程相關(guān)的清理時(shí)必須極其小心。幸運(yùn)的是,在大多數(shù)程序中,調(diào)用Loadlibrary的線程與調(diào)用Freelibrary的線程是同一個(gè)線程。
? ? ? ? 現(xiàn)在我們?cè)賹⑦^(guò)程61換成6,并依次用101(TerminateProcess)、102(ExitProcess)、103(TerminateThread)、104(ExitThread)替換10。我列一下不同點(diǎn)
| 101 | The thread 'Win32 Thread' (0x142c) has exited with code -1 (0xffffffff). The program '[6128] CallDllMain.exe: Native' has exited with code -1 (0xffffffff). | 執(zhí)行線程(0x142c)和進(jìn)程退出時(shí)未對(duì)任何加載的DLL調(diào)用DllMain。 沒(méi)有對(duì)主線程退出的捕獲。 |
| 102 | The thread 'Win32 Thread' (0x1214) has exited with code -1 (0xffffffff). Dll2 TID:4660 DLL_PROCESS_DETACH Dll1 TID:4660 DLL_PROCESS_DETACH The program '[2576] CallDllMain.exe: Native' has exited with code -1 (0xffffffff). | 主進(jìn)程(0x1214) 提前意外關(guān)閉,未對(duì)任何加載的DLL調(diào)用DllMain。 執(zhí)行線程(4660)退出時(shí)對(duì)加載了的DLL調(diào)用了其DllMain的DLL_PROCESS_DETACH。 |
| 103 | The thread 'Win32 Thread' (0x81c) has exited with code -1 (0xffffffff). Proceess Exit Dll2 TID:2356 DLL_PROCESS_DETACH Dll1 TID:2356 DLL_PROCESS_DETACH The program '[5860] CallDllMain.exe: Native' has exited with code 0 (0x0). | 執(zhí)行線程(0x81c)退出時(shí)未對(duì)任何加載的DLL調(diào)用DllMain。 主進(jìn)程(2356)退出時(shí)對(duì)加載了的DLL調(diào)用了其DllMain的DLL_PROCESS_DETACH。 |
| 104 | Dll2 TID:5600 DLL_THREAD_DETACH Dll1 TID:5600 DLL_THREAD_DETACH The thread 'Win32 Thread' (0x15e0) has exited with code -1 (0xffffffff). Proceess Exit Dll2 TID:632 DLL_PROCESS_DETACH Dll1 TID:632 DLL_PROCESS_DETACH The program '[284] CallDllMain.exe: Native' has exited with code 0 (0x0). | 執(zhí)行線程(5600)退出時(shí)對(duì)加載的DLL調(diào)用了DllMain,且原因是DLL_THREAD_DETACH。 主進(jìn)程(632)退出時(shí)對(duì)加載了的DLL調(diào)用了其DllMain的DLL_PROCESS_DETACH。 |
? ? ? ? 從以上我們可以看出Terminate(101、103)類型函數(shù)比Exit(102、104)類型函數(shù)暴力。
? ? ? ? 102例子中我們看到主線程退出后,子線程還在正常工作的場(chǎng)景,可以想象,可能是ExitProcess是直接TerminateThread主線程了。總結(jié)如下:
? ? ? ? 八 TerminateProcess 將導(dǎo)致線程和進(jìn)程在退出時(shí)不對(duì)未卸載的DLL進(jìn)行DllMain調(diào)用。
? ? ? ? 九 ExitProcess將導(dǎo)致主線程意外退出,子線程對(duì)未卸載的DLL進(jìn)行了DllMain調(diào)用,且調(diào)用原因是DLL_PROCESS_DETACH。(《windows核心編程》上是說(shuō),調(diào)用ExitProcess函數(shù)的線程將負(fù)責(zé)執(zhí)行DllMain函數(shù)的代碼。(DLL_PROCESS_DETACH))
? ? ? ? 十?ExitThread是最和平的退出方式,它會(huì)讓線程退出前對(duì)未卸載的DLL調(diào)用DllMain。
? ? ? ? 我們?cè)倏紤]下DisableThreadLibraryCalls函數(shù)對(duì)DllMain函數(shù)的調(diào)用的影響。我們?cè)贒ll1的DllMain中加入DisableThreadLibraryCalls(hModule);我們觀察下結(jié)果
| ? | MainTid:7760 | ? |
| 0 | CreatThread A ThreadA 7992 | ? |
| 1 | LoadLibraryA Dll1 TID:7760 DLL_PROCESS_ATTACH | 加載DLL1,執(zhí)行線程調(diào)用其DllMain,原因是DLL_PROCESS_ATTACH。 |
| 2 | CreatThread B ThreadB 6684 | 線程B創(chuàng)建不會(huì)對(duì)DLL1調(diào)用DllMain了。因?yàn)镈LL1中調(diào)用了DisableThreadLibraryCalls。 |
| 3 | LoadLibraryA Dll2 Dll2 TID:7760 DLL_PROCESS_ATTACH | 加載DLL2。執(zhí)行線程調(diào)用其DllMain,原因是DLL_PROCESS_ATTACH。 |
| 4 | CreatThread C Dll2 TID:8168 DLL_THREAD_ATTACH ThreadC 8168 | 線程C創(chuàng)建不會(huì)對(duì)DLL1調(diào)用DllMain了。但是會(huì)對(duì)沒(méi)有調(diào)用過(guò)DisableThreadLibraryCalls的DLL2調(diào)用DllMain。 |
| 5 | CreatThread D Dll2 TID:1848 DLL_THREAD_ATTACH ThreadD 1848 | 同上 |
| 6 | TerminateThread D | ? |
| 7 | ThreadA Will Exit Dll2 TID:7992 DLL_THREAD_DETACH | 線程A退出,不會(huì)對(duì)DLL1調(diào)用DllMain了。但是會(huì)對(duì)沒(méi)有調(diào)用過(guò)DisableThreadLibraryCalls的DLL2調(diào)用DllMain。 |
| 8 | ThreadB Will Exit Dll2 TID:6684 DLL_THREAD_DETACH | 同上 |
| 9 | ThreadC Will Exit Dll2 TID:8168 DLL_THREAD_DETACH | 同上 |
| 10 | Dll2 TID:7760 DLL_THREAD_DETACH Proceess Exit Dll2 TID:8096 DLL_PROCESS_DETACH Dll1 TID:8096 DLL_PROCESS_DETACH | 執(zhí)行線程(7760)出前不會(huì)對(duì)DLL1調(diào)用DllMain了。 進(jìn)程退出前,主線程會(huì)對(duì)DLL1和DLL2調(diào)用DllMain。 |
? ? ? ? 通過(guò)以上我們可以再得出一個(gè)結(jié)論
? ? ? ? 十一 線程的創(chuàng)建和退出不會(huì)對(duì)調(diào)用了DisableThreadLibraryCalls的DLL調(diào)用DllMain。
? ? ? ? 最后,我們考慮下LoadLibrary和Freelibrary對(duì)DllMain的影響。我將在兩個(gè)線程中嘗試多次LoadLibrary同一個(gè)Dll,多次Freelibrary同一個(gè)Dll。
PrintLog("LoadLibraryA1\n");
HMODULE hDll1 = LoadLibraryA("DLL1");
WAIT();
PrintLog("LoadLibraryA2\n");
hDll1 = LoadLibraryA("DLL1");
WAIT();
PrintLog("LoadLibraryA3\n");
hDll1 = LoadLibraryA("DLL1");
WAIT();
CreateThread( NULL, NULL, ThreadFun, NULL, 0, NULL );
Sleep(35000);
PrintLog("FreeLibrary1\n");
FreeLibrary(hDll1);
WAIT();
PrintLog("FreeLibrary2\n");
FreeLibrary(hDll1);
WAIT();
PrintLog("FreeLibrary3\n");
FreeLibrary(hDll1);
WAIT();
其結(jié)果是
LoadLibraryA1
Dll1 TID:4620 DLL_PROCESS_ATTACH
LoadLibraryA2
LoadLibraryA3
Dll1 TID:5560 DLL_THREAD_ATTACH 子線程創(chuàng)建時(shí)調(diào)用的
MainTid:5560
……
FreeLibrary1
FreeLibrary2
FreeLibrary3
Dll1 TID:4620 DLL_PROCESS_DETACH
子線程創(chuàng)建時(shí)調(diào)用的
MainTid:5560
……
FreeLibrary1
FreeLibrary2
FreeLibrary3
Dll1 TID:4620 DLL_PROCESS_DETACH
? ? ? ? 我們發(fā)現(xiàn)第一句LoadLibrary對(duì)DllMain產(chǎn)生了調(diào)用DLL_PROCESS_ATTACH,而第二三句LoadLibrary不會(huì)對(duì)DllMain產(chǎn)生任何調(diào)用(《windows核心編程》系統(tǒng)不會(huì)讓進(jìn)程的主線程用DLL_THREAD_ATTACH值調(diào)用DLLMain函數(shù)。系統(tǒng)不會(huì)讓用DLL_PROCESS_ATTACH來(lái)調(diào)用該DLL的DllMain函數(shù)的線程不會(huì)得到DLL_THREAD_ATTACH通知);第一二次FreeLibrary對(duì)DllMain沒(méi)有產(chǎn)生調(diào)用,而第三次FreeLibrary對(duì)DllMain產(chǎn)生了DLL_PROCESS_DETACH調(diào)用。
? ? ? ? 可以見(jiàn)得,在一個(gè)線程中對(duì)DLL產(chǎn)生了DllMain調(diào)用后,就不會(huì)因?yàn)長(zhǎng)oadlibrary再發(fā)生DllMain的調(diào)用。
? ? ? ? 我們前兩次FreeLibrary不會(huì)對(duì)DllMain進(jìn)行調(diào)用,而第三次就是DLL_PROCESS_DETACH。同樣這個(gè)線程中LoadLibraryA也被調(diào)用了三次。可以想象LoadLibraryA和FreeLibrary之間存在一個(gè)計(jì)數(shù)器的關(guān)系(LoadLibraryA加計(jì)數(shù)器,FreeLibrary減計(jì)數(shù)器)。正如《windows核心編程》上所說(shuō):當(dāng)系統(tǒng)第一次將一個(gè)DLL映射到進(jìn)程的地址空間中時(shí)……如果之后一個(gè)線程在調(diào)用Loadlibrary(Ex)來(lái)載入一個(gè)已經(jīng)被映射到進(jìn)程的地址空間的DLL,那么操作系統(tǒng)只不過(guò)是遞增該DLL的使用計(jì)數(shù),而不會(huì)再次用DLL_PROCESS_ATTACH來(lái)調(diào)用DllMain函數(shù)。
? ? ? ? 本文中介紹了經(jīng)過(guò)幾輪實(shí)驗(yàn),得出了11條規(guī)律。我們之后研究DllMain導(dǎo)致的死鎖,將用到這些規(guī)律。
總結(jié)
以上是生活随笔為你收集整理的DllMain中不当操作导致死锁问题的分析--进程对DllMain函数的调用规律的研究和分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: DllMain中不当操作导致死锁问题的分
- 下一篇: DllMain中不当操作导致死锁问题的分