问题的目的:对DllMain
的实际检查。
这是“普通”的知识,你不应该做太多的DllMain,有明确的事情你永远不能做,一些最佳实践。
我现在无意中发现了一颗新的宝石,这对我来说没什么意义:(空)(我的)
在处理
DLL_PROCESS_DETACH
时,只有在动态卸载DLL (lpReserved
参数为NULL)时,DLL才会释放堆内存等资源。如果进程正在终止( lpvReserved参数为非空),则进程中的所有线程(除了当前线程--)已经退出或已通过调用ExitProcess
函数显式终止,这可能会给留下一些进程资源,如堆E 114
,处于不一致的状态E 215
。在这种情况下,DLL清理资源是不安全的。相反,DLL应该允许操作系统恢复内存。
由于全局C++对象在DllMain/DETACH期间被清理,这意味着全局C++对象不能释放任何动态内存,因为堆可能处于不一致的状态。/当DLL“静态地链接”到可执行文件时。/当然不是我所看到的--各种库(我们的库和第三方库)的全局C++对象(如果有)在它们的析构函数中分配和释放非常好。(禁止其他订购错误,o.c.)
那么,这个警告所针对的具体技术问题是什么呢?
由于该段提到线程终止,当某些线程未被正确清理时,是否会出现堆损坏问题?
发布于 2018-12-17 18:04:59
一般情况下,ExitProcess
API会执行以下操作:
GetProcessHeap()
返回)通过HeapLock
(GetProcessHeap())
(确定,当然通过RtlLockHeap
) (这是避免死锁的非常重要的一步)NtTerminateProcess(0, 0)
)LdrShutdownProcess
-在这个api加载器中遍历加载的模块列表并发送带有lpvReserved
非空的lpvReserved
。NtTerminateProcess(NtCurrentProcess(), ExitCode )
。这里的问题是线程将终止在任意位置。例如,线程可以从任何堆中分配或释放内存,并在其终止时位于堆临界段中。因此,如果DLL_PROCESS_DETACH
期间的代码试图将一个块从同一个堆中释放出来,那么当试图进入这个堆的关键部分时,它会死掉(当然,堆实现使用它)。
请注意,这不影响主进程堆,因为在HeapLock
终止所有线程(当前除外)之前,我们为其调用。这样做的目的是:我们在这个调用中等待,直到所有其他线程从进程堆关键部分退出,并且在获得关键部分之后,其他线程都不能进入它--因为主进程堆被锁定了。
因此,当我们在锁定主堆之后终止线程时--我们可以确保在主堆临界段或堆结构中没有其他被杀死的线程处于不一致的状态。多亏了RtlLockHeap
的电话。但这只与主进程堆有关。进程中的任何其他堆都不会被锁定。因此,它们可以在DLL_PROCESS_DETACH
期间处于不一致的状态,也可以由已经终止的线程独占地获取。
所以-在这里使用HeapFree
进行GetProcessHeap
或者说LocalFree
是安全的(尽管没有文档说明)。
如果在进程终止期间调用HeapFree
,则对任何其他堆使用DllMain
是不安全的。
另外,如果您通过多个线程使用另一个自定义数据结构--它可能处于不一致的状态,因为另一个线程(可以使用它)在任意点终止。
因此,本文警告说,当lpvReserved参数为非空(在进程终止期间调用DllMain的意思)时,您在清理资源时需要特别小心。总之,当进程死亡时,操作系统将释放所有内部内存分配。
发布于 2018-12-17 21:13:25
作为RbMm出色答案的补充,我将添加一段来自ExitProcess
的引用,它比DllMain文档做得好得多,可以解释为什么堆操作(或任何操作,真的)会受到损害:
如果进程中的一个终止线程持有一个锁,而其中一个加载DLL中的DLL分离代码试图获取相同的锁,则调用
ExitProcess
将导致死锁。相反,如果进程通过调用TerminateProcess终止,则进程附加到的DLL不会被通知进程终止。因此,如果您不知道进程中所有线程的状态,最好调用TerminateProcess
而不是ExitProcess
。注意,从应用程序的主函数返回将导致对ExitProcess
的调用。
因此,这可以归结为:应用程序有“失控”线程,可以容纳任何锁,(CRT)堆锁就是一个突出的例子,当您需要访问与“失控”线程使用的相同结构(例如堆)时,在关机期间遇到了一个大问题。
这表明你应该以一种控制的方式关闭你的所有线程。
https://stackoverflow.com/questions/53818859
复制相似问题