_Exit 在 C++ 中的行为如何程序?
C99 提供了“立即”退出的_Exit
函数,尽管它确实 可能会关闭文件描述符。 Unix/POSIX 通过强制关闭所有 fd 而不刷新来扩展此行为(并提供同义词 _exit
)。
从 C++ 程序调用时,这些函数是否会调用静态对象的析构函数? C++ 标准是否对 _Exit
做出任何保证?
(受到这个问题的启发;我突然想知道 C++ 中典型的 fork
-exec
-_exit
惯用法会发生什么。)
C99 offers the _Exit
function, which exits "immediately", although it does may close file descriptors. Unix/POSIX extends this behavior by mandating the closing of all fd's without flushing (and offers the synonym _exit
).
Will these functions call destructors for static
objects when called from a C++ program? Does the C++ standard make any guarantees about _Exit
?
(Inspired by this question; I suddenly wondered what happens in the typical fork
-exec
-_exit
idiom in C++.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
它根本不存在于标准 C++ 中,因此无法保证。
计划将其包含在 C++0x 中。其中指定(§18.5):
后续:
ISO 于 2011 年 8 月 12 日批准了 C++0x。
It simply doesn't exist in standard C++, so there are no guarantees.
It is planned for inclusion in C++0x. That specifies (§18.5):
Followup:
ISO approved C++0x on August 12, 2011.
首先,任何形式的程序退出都不会自动调用堆对象的析构函数(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< /code> 而
_exit()
只是调用操作系统调用来立即终止进程。这意味着主要区别是:_exit()
指定退出代码abort()
可能会被信号处理程序捕获,abort()
可能会导致核心转储或类似情况。在
fork()/exec()
模式中,_exit()
可能会最好,以避免核心转储的可能性。First, no form of program exit will automatically call destructors for heap objects (implied in ISO/IEC 14882:1998(E) 12.4.10).
Calling
exit()
will not call destructors for objects with automatic duration, as it does not return through their enclosing scopes (3.6.1.4). However, destructors for static objects will be called, in reverse order of construction (18.3.8).Calling
abort()
does not call any destructors for any type of object, nor does it callatexit()
registered functions (18.3.3). The C++ standard copy I have here is a bit dated and does not mention_exit
or_Exit
directly, but I'd imagine that, if present, they should behave the same - that is, not calling any destructors. In particular, in the C99 standard,_Exit()
skipsatexit
handlers (it is implementation defined whether stream buffers are flushed, open streams are closed, or temporary files removed).Further note that
abort()
can be cancelled by trapping signalSIGABRT
(ISO/IEC 9899:1999 (E) 7.20.4.1.2 - I only have C99 here but I expect it would be the same in the version referenced by C++)._Exit()
cannot.On a more practical note, on most unix implementations of
abort()
and_exit()
,abort()
raises aSIGABRT
while_exit()
simply calls an operating system call to terminate the process immediately. This means that the main differences are:_exit()
abort()
may be trapped by a signal handlerabort()
may result in a core dump or similarIn a
fork()/exec()
pattern,_exit()
would probably be preferable, to avoid the possibility of core dump.从技术上讲,
_Exit
不是由 C++ 标准定义的,因此您甚至无法从 100% 可移植的 C++ 程序中调用它。 C++03 标准通过引用合并了 C89 标准(也称为 C90 或 ANSI C),而_Exit
仅在较新的 C99 标准中定义。我不确定即将推出的 C++0x 标准包含哪个版本的 C,但我猜测它是基于 C99 的。但无论如何,以下是相关语言标准中的相关条款:
_Exit
不保证关闭文件描述符。来自 C99 §7.20.4.4/2(强调我的):回想一下,实现定义意味着实现(即也就是说,编译器工具链和运行时环境)可以选择做任何它想做的事情,但是它必须记录它所做的事情。
来自 C++03 §3.6.3/1:
§3.6.3/4:
实际上,在大多数实现中,全局对象析构函数是通过
atexit
,所以您将看到_Exit
不会调用全局对象的析构函数,尽管不能保证此行为(因为_Exit
和 C++ 不保证两者都存在用同一种语言)。Technically,
_Exit
is not defined by the C++ standard, so you can't even call it from a 100% portable C++ program. The C++03 standard incorporates by reference the C89 standard (aka C90 or ANSI C), whereas_Exit
is only defined in the newer C99 standard. I'm not sure which version of C the upcoming C++0x standard incorporates, but I would guess that it's based on C99.In any case, though, here are the relevant clauses from the relevant language standards:
_Exit
is not guaranteed to close file descriptors. From C99 §7.20.4.4/2 (emphasis mine):Recall that implementation-defined means that the implementation (that is, the compiler toolchain and runtime environment) can choose to do whatever it wants, but it must document what it does.
From C++03 §3.6.3/1:
§3.6.3/4:
Practically, in most implementations, global object destructors are implemented via
atexit
, so what you will see is that_Exit
will not call the destructors for global objects, although this behavior is not guaranteed (since_Exit
and C++ are not guaranteed to both exist in the same language).请注意,虽然 C++ 没有指定 _Exit 并且 C99 让它由实现定义是否刷新缓冲区,但 POSIX 要求 它不刷新缓冲区(因为这会破坏
_exit
/_Exit
,即处理fork
后execve
失败)。由于 POSIX 本身并不与 C++ 标准保持一致,也不在任何事情上遵循它们,因此我认为 C++ 标准的未来版本不太可能尝试改变这一切。它可能会保留_Exit
未指定或指定它是实现定义的。Note that while C++ does not specify
_Exit
and C99 leaves it implementation-defined whether it flushes buffers, POSIX requires that it not flush buffers (since this would break the main usage of_exit
/_Exit
, i.e. handling failure ofexecve
afterfork
). As POSIX does not align itself with C++ standards or defer to them on anything, I think it's very unlikely that a future version of the C++ standard would try to change any of this. It will probably either leave_Exit
unspecified or specify that it's implementation-defined.C++0x 定义了一个名为 std::quick_exit 的新函数,用于终止进程不调用任何析构函数。刚查了一下,g++-4.4.5已经提供了。
C++0x defines a new function called std::quick_exit that terminates a process without calling any destructors. Just checked, g++-4.4.5 already provides it.
这里有一个有趣的分析与并发和对象销毁的关系。据我所知,析构函数不会被调用。现行标准中没有任何相关内容。
There is an interesting analysis here in relation with concurrency and object destruction. As far as I know, destructors will not be called. There is nothing about it in the current standard.
静态析构函数的调用是根据 atexit 定义的。 _exit(或 _Exit)被定义为不运行 atexit 处理程序。因此任何实现都不应该调用静态析构函数。
调用 exit() 时甚至不会调用自动析构函数。
因此,C++ 的 _Exit 语义的任何合理定义都不会运行析构函数。
Calling of static destructors is defined in terms of atexit. _exit (or _Exit) is defined not to run atexit handlers. So static destructors should not be called by any implementation.
Automatic destructors are not even called when calling exit().
So any sane definition of _Exit semantics for C++ would not run destructors.
我在 Mac OS 上使用 gcc 进行了快速测试,我的析构函数没有被调用。
另一方面,
exit(0)
调用globalA
的析构函数。I did a quick test with gcc on Mac OS and my destructors didn't get called.
exit(0)
on the other hand callsglobalA
's destructor.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()
时,此类对象不会被破坏。 C++ 2003 标准中没有解决_exit()
问题,但 POSIX 文档中描述了它旨在绕过特定于语言的清理的事实。 C++ 标准中规定的内容和未规定的内容进一步证实了这种效果。fork()
,exec()
, and_exit()
are all defined by POSIX and they pre-date C99's_Exit()
by many years. Programs that usefork
/exec
/_exit
are not portable to every system that supports C++.With regard to
_exit()
specifically, it is an operating system call that (under POSIX) will close files and terminate the process directly (but not necessarily quickly). This would bypass any C++ mechanisms for calling destructors.Even with
_Exit()
or similar being provided by C++0x, I doubt if there would be much reason to use that in conjunction with fork. It likely just provides broader portability for a "quick-exit" in other contexts. That functionality is already covered by_exit()
if you are using the POSIX API.Program termination is addressed in C++2003 section [3.6.3]. It says that static objects are destructed implicitly when
main()
returns and whenexit()
is called. It also says that such objects are NOT destructed whenabort()
is called._exit()
isn't addressed in the C++ 2003 standard, but the fact that it is meant to bypass language-specific cleanup is described in the POSIX documentation. That effect is further substantiated by what is stated and by what is NOT stated in the C++ standard.