你怎么知道main是不是已经退出?
Posted
技术标签:
【中文标题】你怎么知道main是不是已经退出?【英文标题】:How do you know whether main has exited?你怎么知道main是否已经退出? 【发布时间】:2015-05-14 08:54:47 【问题描述】:在 C 和 C++ 中,atexit
函数要么在 exit
内部调用,要么在 main
返回之后(名义上调用 exit
:__libc_start_main(argc,argv) __libc_constructors(); exit(main(argc,argv));
)。
有没有办法确定我们是否在退出序列中? C++ 全局和局部静态的析构函数在atexit
注册,所以你的代码当然可以在这个阶段被调用。 (有趣的是,在某些平台上,如果您尝试在 exit
中创建 C++ 本地静态对象,它会在退出锁上死锁!)
到目前为止我最好的尝试如下:
static bool mainExited = false;
static void watchMain()
static struct MainWatcher
~MainWatcher() mainExited = true;
watcher;
当你想观察退出时,你调用watchMain()
,mainExited
随时告诉你退出序列是否已经开始——当然,如果稍后初始化的本地静态对象正在破坏!
是否可以改进该技术以纠正此问题,或者是否有其他方法可行?
除此之外 - 用例!
虽然从语言的角度来看这个问题很有趣(有点像“我可以判断我是否在 catch
块内吗?”),但概述用例也很有用。我在编写一些代码时遇到了这个问题,这些代码将在加载和不加载 JVM 的情况下运行(直接调用或通过 JNI 调用)。 JVM退出后,调用Catexit
处理程序,如果类加载器没有卸载JNI共享库,则不调用JNI_OnUnload
。
由于共享库的对象可以通过显式销毁(并且应该释放它们的资源)和退出时的清理来销毁,我需要安全地区分这两种情况,因为当我们到达出口时 JVM 已经消失了代码!基本上没有一点嗅探,我无法在 JNI 规范/文档中找到一个共享库来知道 JVM 是否仍然存在,如果它消失了,那么尝试释放我们拥有的引用肯定是错误的到 Java 对象。
【问题讨论】:
你能解释一下这个用例吗? 在 main 中创建一个记录其销毁的对象。如果它是第一个创建的对象,它的析构函数将是 main 返回之前调用的最后一个函数。 @Benjamin 我记得在main
中创建的对象不会在exit
中被破坏,只有全局和局部静态?那么您是否建议在 main 内部使用本地静态 - 在这种情况下,这与我的解决方案几乎相同。
为什么不在main
中使用本地非静态对象来检测您是否要离开main
?
我这里没有“现代 C++ 设计”,但 Andrei Alexandrescu 在他的书中对这类 post-main 问题进行了详细分析。
【参考方案1】:
这里真正的问题是您列出的所有权语义是混乱的。 JVM 有点拥有您的共享库,但也有点不拥有。你有一堆对 Java 对象的引用,有时你需要清理它们,但有时你不需要。
这里真正的解决方案是不将 Java 对象的引用保留为全局变量。然后,无论出于何种原因卸载库时,您都不需要知道 JVM 是否仍然存在。只需保留 Java 引用的对象内部对 Java 对象的引用,然后让 JVM 关心是否需要释放它们。
换句话说,首先不要让自己负责清理退出。
【讨论】:
【参考方案2】:您的观察者不需要依赖任何静态初始化顺序:
#include <iostream>
struct MainWatcher // : boost::noncopyable
enum MainStatus before, during, after ;
MainWatcher(MainStatus &b): flag(b) flag = during;
~MainWatcher() flag = after;
MainStatus &flag;
;
//////////////////////////////////////////////////////////////////////
// Test suite
//////////////////////////////////////////////////////////////////////
// note: static data area is zero-initialized before static objects constructed
MainWatcher::MainStatus main_flag;
char const *main_word()
switch(main_flag)
case MainWatcher::before: return "before main()";
case MainWatcher::during: return "during main()";
case MainWatcher::after: return "after main()";
default: return "(error)";
struct Test
Test() std::cout << "Test created " << main_word() << "\n";
~Test() std::cout << "Test destroyed " << main_word() << "\n";
;
Test t1;
int main()
MainWatcher watcher(main_flag);
// rest of code
Test t2;
【讨论】:
不错的尝试,但如果exit
被调用怎么办?不过你是对的,如果你可以修改 main,那么就可以在调用所有静态析构函数之前检测到 main 的退出。
如果调用了exit
函数,则程序被认为在没有main
返回的情况下终止。您的标题可以通过两种方式阅读;你的意思是“你怎么知道 main 是否已经返回?”,或者“你怎么知道 atexit
处理程序是否已被执行?” (或者别的什么——有办法在不运行 atexit 处理程序的情况下终止程序?)
请注意,通常静态析构函数不需要使用atexit
机制
似乎确实有some issues 围绕 gcc,以及共享库中静态对象的析构函数。
对于我必须支持的所有六个 Unix 平台,libc 都支持静态析构函数——该问题中的发布者使用的是未指定的平台,并且可能会做一些奇怪的事情。在 linux 和 OS X 上,静态对象的析构函数当然没有问题。对不起,标题有点模棱两可;不过,第一段清楚地说明了问题:“有没有办法找出我们是否在退出序列内?”以上是关于你怎么知道main是不是已经退出?的主要内容,如果未能解决你的问题,请参考以下文章