1

问题目的:DllMain.

在 DllMain 中您不应该做太多事情是“常识”,有些事情绝对不能做,一些最佳实践

我现在偶然发现了文档中的一个新宝石,这对我来说毫无意义:(emph。我的)

处理时,只有在动态卸载 DLL(参数为 NULL)时DLL_PROCESS_DETACH,DLL 才应释放堆内存等资源 。lpReserved如果进程正在终止(lpvReserved 参数为非 NULL),则除当前线程外,进程中的所有线程要么已经退出,要么已被函数调用显式终止ExitProcess,这可能会留下 一些进程资源,例如不一致的状态。在这种情况下,DLL 清理资源是不安全的。相反,DLL 应该允许操作系统回收内存。

由于全局 C++ 对象在 DllMain/DETACH 期间被清除,这意味着全局 C++ 对象不得释放任何动态内存,因为堆可能处于不一致状态。/ 当 DLL “静态链接”到可执行文件时。/ 当然不是我在那里看到的 - 各种(我们的和第三方的)库的全局 C++ 对象(如果有)在它们的析构函数中分配和解除分配就好了。(除非其他订购错误,oc)

那么,这个警告针对的是什么具体的技术问题呢?

既然段落中提到了线程终止,那么当某些线程没有正确清理时会出现堆损坏问题吗?

4

2 回答 2

1

ExitProcessAPI 通常执行以下操作:

  • 进入装载机锁临界区
  • GetProcessHeap()通过 (好的,当然是通过)锁定主进程堆(由返回)(这是避免死锁的非常重要的步骤)HeapLock(GetProcessHeap())RtlLockHeap
  • 然后终止进程中的所有线程,除了当前线程(通过调用NtTerminateProcess(0, 0)
  • 然后调用LdrShutdownProcess- 在这个 api 加载器中,通过加载的模块列表遍历并发送DLL_PROCESS_DETACHlpvReserved空值。
  • finally 调用NtTerminateProcess(NtCurrentProcess(), ExitCode )终止进程。

这里的问题是线程在任意位置终止。例如,线程可以在任何堆中分配或释放内存,并在它终止时位于堆临界区内。结果,如果代码在DLL_PROCESS_DETACH尝试从同一个堆中释放一个块时,它会在尝试进入该堆的临界区时死锁(当然,如果堆实现使用它)。

请注意,这不会影响主进程堆,因为我们在终止所有线程(当前线程除外)之前HeapLock调用它。这样做的目的:我们在这个调用中等待,直到所有其他线程退出进程堆临界区,并且在我们获得临界区之后,没有其他线程可以进入它 - 因为主进程堆被锁定。

因此,当我们在锁定主堆后终止线程时 - 我们可以确定没有其他被杀死的线程在主堆临界区或处于不一致状态的堆结构内。谢谢RtlLockHeap来电。但这仅与主进程堆有关。进程中的任何其他堆都不会被锁定。因此,这些可能在期间处于不一致状态,DLL_PROCESS_DETACH或者可以由已终止的线程独占获取。

所以 - 在这里使用HeapFreeforGetProcessHeap或 sayLocalFree是安全的(但没有记录)。

如果在进程终止期间调用,则HeapFree用于任何其他堆都是不安全的。DllMain

此外,如果您由多个线程使用另一个自定义数据结构 - 它可能处于不一致状态,因为另一个线程(可以使用它)在任意点终止。

因此,此注释警告说,当 lpvReserved参数为非 NULL时(意味着在进程终止期间调用 DllMain),您在清理资源时需要特别小心。无论如何,当进程死亡时,操作系统将释放所有内部内存分配。

于 2018-12-17T18:04:59.550 回答
0

作为 RbMm 出色答案的附录,我将ExitProcess在解释为什么堆操作(或任何操作,真的)会受到损害时添加一个比 DllMain 文档做得更好的引述:

如果进程中已终止的线程之一持有锁,并且其中一个加载的 DLL 中的 DLL 分离代码尝试获取相同的锁,则调用ExitProcess会导致死锁。相反,如果进程通过调用TerminateProcess 终止,则不会通知该进程所附加到的 DLL 进程终止。因此,如果您不知道进程中所有线程的状态,最好TerminateProcess调用ExitProcess. 请注意,从应用程序的主函数返回会导致调用ExitProcess.

因此,这一切都归结为:如果您的应用程序具有可能持有任何锁的“失控”线程,(CRT)堆锁就是一个突出的例子,当您需要访问相同的结构时,您在关机期间遇到了一个大问题(例如堆),您的“失控”线程正在使用。

这只是表明您应该以受控方式关闭所有线程。

于 2018-12-17T21:13:25.500 回答