_Exit如何在C ++程序中运行?

时间:2011-01-22 17:05:33

标签: c++ c unix destructor exit

C99提供_Exit函数,它会立即退出,但 可能会关闭文件描述符。 Unix / POSIX通过强制关闭所有fd而不刷新来扩展此行为(并提供同义词_exit)。

从C ++程序调用时,这些函数会调用static个对象的析构函数吗? C ++标准是否对_Exit做出任何保证?

(灵感来自this question;我突然想知道C ++中典型的fork - exec - _exit成语会发生什么。)

9 个答案:

答案 0 :(得分:11)

首先,没有任何形式的程序退出会自动调用堆对象的析构函数(隐含在ISO / IEC 14882:1998(E)12.4.10中)。

调用exit()不会为具有自动持续时间的对象调用析构函数,因为它不会通过其封闭范围返回(3.6.1.4)。但是,将按照构造的相反顺序(18.3.8)调用静态对象的析构函数

调用abort()不会为任何类型的对象调用任何析构函数,也不会调用atexit()注册函数(18.3.3)。我在这里的C ++标准副本有点过时,并没有直接提到_exit_Exit,但我想,如果存在,它们的行为应该相同 - 也就是说,不调用任何析构函数。特别是,在C99标准中,_Exit()跳过atexit个处理程序(实现定义是否刷新流缓冲区,关闭打开的流,还是删除临时文件)。

进一步注意abort()可以通过捕获信号SIGABRT来取消(ISO / IEC 9899:1999(E)7.20.4.1.2 - 我这里只有C99,但我希望它会是在C ++引用的版本中也是如此)。 _Exit()不能。

更实际的说明,在abort()_exit()的大多数unix实现中,abort()引发SIGABRT_exit()只调用操作系统调用立即终止该过程。这意味着主要的区别是:

  • 您可以为_exit()
  • 指定退出代码
  • abort()可能被信号处理程序
  • 困住
  • 根据系统配置,操作系统和ulimits,abort()可能会导致核心转储或类似情况

fork()/exec()模式中,_exit()可能更可取,以避免核心转储的可能性。

答案 1 :(得分:10)

它在标准C ++中根本不存在,因此无法保证。

计划包含在C ++ 0x中。这指定(§18.5):

  

函数_Exit(int status)有   这个中的其他行为   国际标准:

     

- 程序终止没有   执行对象的析构函数   自动,线程或静态存储   持续时间而不调用函数   传递给atexit()(3.6.3)。

跟进:

ISO于2011年8月12日批准了C ++ 0x。

答案 2 :(得分:3)

从技术上讲,_Exit不是由C ++标准定义的,因此您甚至无法从100%可移植的C ++程序中调用它。 C ++ 03标准通过引用引入了C89标准(又名C90或ANSI C),而_Exit仅在较新的C99标准中定义。我不确定即将推出的C ++ 0x标准包含哪个版本的C,但我猜它是基于C99的。

无论如何,这里是相关语言标准的相关条款:

_Exit无法保证关闭文件描述符。从C99§7.20.4.4/ 2(强调我的):

  

_Exit函数会导致正常程序终止并将控制权返回给主机环境。调用atexit函数注册的函数或signal函数注册的信号处理程序。返回到主机环境的状态的确定方式与exit功能(7.20.4.3)相同。 是否刷新具有未写入缓冲数据的开放流,关闭打开流,或者删除临时文件是实现定义的。

回想一下实现定义的意味着实现(即编译器工具链和运行时环境)可以选择做任何想做的事情,但它必须记录它的作用

来自C ++03§3.6.3/ 1:

  

静态存储持续时间的初始化对象(在块作用域或命名空间作用域声明)的析构函数(12.4)作为从main返回并由于调用exit(18.3)的结果而被调用。这些对象以其构造函数完成或动态初始化完成的相反顺序销毁。如果对象是静态初始化的,则对象的破坏顺序与对象动态初始化的顺序相同。对于数组或类类型的对象,在构建子对象期间初始化静态存储持续时间的任何本地对象被销毁之前,该对象的所有子对象都将被销毁。

§3.6.3/ 4:

  

调用函数

     

void abort();

     在<cstdlib>中声明的

终止程序,而不对自动或静态存储持续时间的对象执行析构函数,也不调用传递给atexit()的函数。

实际上,在大多数实现中,全局对象析构函数是通过atexit实现的,所以你会看到_Exit不会为全局对象调用析构函数,尽管这种行为不能得到保证(因为不保证_Exit和C ++都存在于同一种语言中。)

答案 3 :(得分:2)

请注意,虽然C ++没有指定_Exit而C99离开它实现定义是否刷新缓冲区,POSIX 要求它不会刷新缓冲区(因为这会破坏主要用法_exit / _Exit,即在execve之后处理fork的失败。由于POSIX没有与C ++标准保持一致或者在任何事情上遵守它们,我认为未来版本的C ++标准不太可能试图改变这一点。它可能要么未指定_Exit,要么指定它是实现定义的。

答案 4 :(得分:2)

C ++ 0x定义了一个名为std::quick_exit的新函数,它终止进程而不调用任何析构函数。刚刚检查过,g ++ - 4.4.5已经提供了它。

答案 5 :(得分:1)

有一个与并发和对象破坏有关的有趣分析here。据我所知,析构函数不会被调用。目前的标准中没有任何内容。

答案 6 :(得分:1)

静态析构函数的调用是根据atexit定义的。 _exit(或_Exit)定义为不运行atexit处理程序。因此,任何实现都不应该调用静态析构函数。

调用exit()时甚至不调用自动析构函数。

因此,C ++的_Exit语义的任何理智定义都不会运行析构函数。

答案 7 :(得分:0)

我在Mac OS上使用gcc进行了快速测试,我的析构函数没有被调用。

struct A
{
    ~A()
    {
        puts("A::~A");
    }
};

A globalA;

int main()
{
    A localA;
    _exit(0); // or _Exit(0)
    return 0;
}
另一方面

exit(0) 调用 globalA的析构函数。

答案 8 :(得分:0)

fork()exec()_exit()都是由POSIX定义的,它们在C99的_Exit()之前已经过了很多年。使用fork / exec / _exit的程序无法移植到支持C ++的每个系统。

关于_exit()具体来说,它是一个操作系统调用(在POSIX下)将关闭文件并直接终止进程(但不一定很快)。这将绕过任何用于调用析构函数的C ++机制。

即使C ++ 0x提供了_Exit()或类似的东西,我也怀疑是否有太多理由将它与fork结合使用。它可能只是为其他环境中的“快速退出”提供了更广泛的可移植性。如果您使用POSIX API,则_exit()已涵盖该功能。

程序终止在C ++ 2003部分[3.6.3]中解决。它表示当main()返回并且调用exit()时,静态对象会被隐式破坏。它还表示在调用abort()时不会破坏此类对象。 {+ 1}}未在C ++ 2003标准中得到解决,但POSIX文档中描述了它旨在绕过特定于语言的清理的事实。通过陈述的内容以及C ++标准中未说明的内容进一步证实了这种效果。

相关问题