_Exit 在 C++ 中的行为如何程序?

发布于 2024-10-14 04:45:49 字数 544 浏览 13 评论 0原文

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

孤单情人 2024-10-21 04:45:49

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

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

函数_Exit(int status)有
在此的额外行为
国际标准:

—程序终止,无需
为以下对象执行析构函数
自动、线程或静态存储
持续时间且不调用函数
传递给 atexit() (3.6.3)。

后续:

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):

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.

她说她爱他 2024-10-21 04:45:49

首先,任何形式的程序退出都不会自动调用堆对象的析构函数(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() 可能会被信号处理程序捕获,
  • 具体取决于系统配置、操作系统和ulimits、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 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.

煮茶煮酒煮时光 2024-10-21 04:45:49

从技术上讲,_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();

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

实际上,在大多数实现中,全局对象析构函数是通过 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):

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).

最好是你 2024-10-21 04:45:49

请注意,虽然 C++ 没有指定 _Exit 并且 C99 让它由实现定义是否刷新缓冲区,但 POSIX 要求 它不刷新缓冲区(因为这会破坏_exit/_Exit,即处理forkexecve失败)。由于 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 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.

野侃 2024-10-21 04:45:49

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.

爱本泡沫多脆弱 2024-10-21 04:45:49

这里有一个有趣的分析与并发和对象销毁的关系。据我所知,析构函数不会被调用。现行标准中没有任何相关内容。

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.

你是年少的欢喜 2024-10-21 04:45:49

静态析构函数的调用是根据 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.

我三岁 2024-10-21 04:45:49

我在 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的析构函数。

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.

梅倚清风 2024-10-21 04:45:49

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 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文