可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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++.)
回答1:
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):
The function _Exit(int status) has
additional behavior in this
International Standard:
— The program is terminated without
executing destructors for objects of
automatic, thread, or static storage
duration and without calling functions
passed to atexit() (3.6.3).
Followup:
ISO approved C++0x on August 12, 2011.
回答2:
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 call atexit()
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()
skips atexit
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 signal SIGABRT
(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 a SIGABRT
while _exit()
simply calls an operating system call to terminate the process immediately. This means that the main differences are:
- You can specify an exit code for
_exit()
abort()
may be trapped by a signal handler
- Depending on system configuration, OS, and ulimits,
abort()
may result in a core dump or similar
In a fork()/exec()
pattern, _exit()
would probably be preferable, to avoid the possibility of core dump.
回答3:
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):
The _Exit
function causes normal program termination to occur and control to be returned to the host environment. No functions registered by the atexit
function or signal handlers registered by the signal
function are called. The status returned to the host environment is determined in the same way as for the exit
function (7.20.4.3). Whether open streams with unwritten buffered data are flushed, open streams are closed, or temporary files are removed is implementation-defined.
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:
Destructors (12.4) for initialized objects of static storage duration (declared at block scope or at namespace scope) are called as a result of returning from main and as a result of calling exit
(18.3). These objects are destroyed in the reverse order of the completion of their constructor or of the completion of their dynamic initialization. If an object is initialized statically, the object is destroyed in the same order as if the object was dynamically initialized. For an object of array or class type, all subobjects of that object are destroyed before any local object with static storage duration initialized during the construction of the subobjects is destroyed.
§3.6.3/4:
Calling the function
void abort();
declared in <cstdlib>
terminates the program without executing destructors for objects of automatic or static storage duration and without calling the functions passed to atexit()
.
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).
回答4:
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 of execve
after fork
). 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.
回答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.
回答6:
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.
回答7:
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.
回答8:
I did a quick test with gcc on Mac OS and my destructors didn't get called.
struct A
{
~A()
{
puts("A::~A");
}
};
A globalA;
int main()
{
A localA;
_exit(0); // or _Exit(0)
return 0;
}
exit(0)
on the other hand calls globalA
's destructor.
回答9:
fork()
, exec()
, and _exit()
are all defined by POSIX and they pre-date C99's _Exit()
by many years. Programs that use fork
/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 when exit()
is called. It also says that such objects are NOT destructed when abort()
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.