【问题标题】:ProcessExit - order of DLL unloads and staticsProcessExit - DLL 卸载和静态的顺序
【发布时间】:2012-10-17 11:30:46
【问题描述】:

拥有一个隐式加载某些 DLL 和显式加载其他 DLL 的 EXE(LoadLibrary)。这个 EXE 正在执行它的 ExitProcess(剩下的进程中的 1 个线程),并且作为忙于卸载 DLL 的一部分,比如 A.DLL。

A.DLL(不幸的是)有一个静态的。调用 atexit 回调并开始调用此静态的析构函数,留下一系列析构函数,直到析构函数决定进行一些清理并加载 DLL 来执行此操作。此 DLL 执行某些方法,直到由于内存访问冲突而发生崩溃,因为它将在此 DLL 中使用的静态不再存在。查看此静态的堆栈跟踪,它的析构函数已作为其 DLL 卸载的一部分被调用。

发生了什么? DLL 被加载,执行了一些方法,去使用一个静态的(在同一个 DLL 中),但是这个已经被破坏了(静态只在 dll 被卸载时被破坏)。那么它是否处于执行方法的半不确定状态但也正在被破坏?

EXE 似乎在 __tmainCRTStartup 的上下文中,这意味着用户创建的 main 已返回? DLL 是否在用户 main 或 tmainCRTStartup 的上下文中被卸载?

【问题讨论】:

  • 在 .exe 的全局析构函数完成后,DLL 在 tmainCRTStartup 中被关闭。
  • 显式调用 FreeLibrary() 的代码可能会很痛苦,拆除正在运行的程序是一项危险的工作。如果您无法从代码所有者那里获得帮助,则可以调用 exit(0)。请注意 C++11 的 std::quick_exit() 函数,解决了同样的问题。
  • 没有 FreeLibrary 调用,运行时正在卸载 dll 的...
  • 如果进程在 __tmainCRTStartup 的上下文中,那么我相信该进程还没有开始卸载 DLL(这发生在 __tmainCRTStartup 将控制权返回给 Windows 之后)。听起来好像问题与 DLL 的卸载无关,而与处理 atexit 回调的运行时库有关(正如 ElektroKraut 解释的那样)。如果是这样,使用 ExitProcess 而不是退出 main 函数应该可以解决问题 - 前提是您并不真的 需要 调用任何析构函数。当然,这取决于他们在做什么。

标签: c++ windows visual-c++ dll static


【解决方案1】:

就这么简单:静态对象的析构函数按照它们创建的相反顺序调用,这是通过注册 atexit 回调在内部完成的。唯一不同的情况是您手动卸载 (FreeLibrary) DLL。

您描述的问题仅表明您具有循环依赖关系,静态构造函数/析构函数很容易发生这种情况。你应该小心你在析构函数中所做的事情,尤其是此时加载一个 DLL 对我来说似乎很危险。

【讨论】:

  • 我同意你的观点,但是这是遗留代码。我也了解销毁和 atexit 回调的顺序。让我感到困惑的是,我们正在执行一些 dll 代码,但它的静态已被破坏。
  • 正如我所说,这总是会发生在静态对象相互依赖的情况下,特别是因为静态构造函数的顺序是未定义的(对于一个 DLL 中的静态对象也是如此)...
  • A.DLL 正在执行代码,它会使用它的静态(在 A.DLL 中)。这被破坏了。因此,静态所在的同一个 DLL 正在运行代码,但其静态已被破坏。这让我很困惑。
  • 这就是我想象它发生的方式(简化):(1)执行静态构造函数 A(2)加载 DLL(3)执行 DLL 静态构造函数 B(4)您调用 exit (5) 调用 DLL 静态析构函数 ~B (6) 调用静态构造函数 ~A,但尝试使用 B -> 崩溃!
  • 你有一个导致重入的循环依赖。在清理 A 的静态数据期间,您执行调用 A 的代码,假设它的静态数据仍然有效。该假设无效(A 的静态数据正在被破坏),因此您的问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-02
  • 2010-11-12
  • 1970-01-01
  • 1970-01-01
  • 2011-09-11
  • 1970-01-01
相关资源
最近更新 更多