你怎么知道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是不是已经退出?的主要内容,如果未能解决你的问题,请参考以下文章

main函数你到底知道多少

科普:为什么SpringBoot中main方法执行完毕后程序不会直接退出呢

你可能想不到,竟然那么多人不知道退出 Vim 的命令

进入linux的进程管理器后不知道怎么退出来了

从java main方法说开去(转)

我怎么知道一个分支是不是已经合并到 master 中?