为什么这个简单的代码适用于 `exit` 而不适用于 `_exit`?

Why is this simple code working with `exit` and is not working with `_exit`?

请看一下这个示例代码,它使用非常成熟的编程模式将 stdout 重定向到管道。

#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int fd[2];
    pipe(fd);

    pid_t pid = fork();
    if (pid == 0) {
        close(fd[0]);
        dup2(fd[1], 1);
        printf("A string");
        _exit(0);
    }

    close(fd[1]);
    char text[1000];
    size_t size;
    int p = 0;
    while ((size = read(fd[0], text+p, 1)) == 1) {
        p++;
    }
    close(fd[0]);
    text[p] = '[=10=]';
    printf("%s", text);

    return 0;
}

目前的代码无法正常工作。正如@kaylum 在评论中正确建议的那样,在子进程中调用 exit 而不是 _exit 可以使代码正常工作。

exit() 刷新所有打开的流作为其终止的一部分,而 _exit() 不刷新 - 因此调用 _exit().

时任何缓冲输出都会丢失

您可以 "work" 要么:

1) 通过调用 fflush(stdout); 刷新缓冲输出 调用 _exit()
之前 2) 在 main() 的开头使用 setbuf(stdout, 0); 禁用标准输出的缓冲。

POSIX 要求流由 exit():

刷新

The exit() function shall then flush all open streams with unwritten buffered data and close all open streams. Finally, the process shall be terminated [CX] with the same consequences as described in Consequences of Process Termination.

同样,要求流不是_exit()刷新:

The _Exit() [CX] and _exit() functions shall not call functions registered with atexit() nor any registered signal handlers. [CX] Open streams shall not be flushed. Whether open streams are closed (without flushing) is implementation-defined. Finally, the calling process shall be terminated with the consequences described below.

通常 _ 保留用于实现。 _Exit 是在 C99 中引入的,因此您可能必须使用一个开关来使您的编译器符合 C99。

_exit 终止进程而不执行 exit(或从 main 返回)执行的常规清理步骤。建议在某些情况下在 child 进程中使用,因为它可以避免执行不正确或不必要的清理;当分叉一个 worker 来执行一个短任务时,parent 任务可能没有刷新 I/O 个缓冲区,并且使用 _exit 可以防止 parent 写入相同的缓冲数据和 child,神秘地重复输出。它还会阻止 atexit 处理程序等的执行。

在 C++ 程序中,it also bypasses stack unwinding, so RAII cleanup (destructor invocation) doesn't occur either;对于纯粹的本地资源来说几乎毫无意义(child 退出得更快一些,但这通常无关紧要),但如果清理影响外部可见状态则很重要。

exit 是一个 C 级函数,它刷新然后关闭每个 C 级流(stdin、stdout 和 stderr 是 C 级流)。

_exit 是一个 POSIX 级函数,它关闭每个系统级流(0、1、2 等描述符是系统级流)。在这种情况下,缓冲是一个系统内部问题。

你做了一个重定向让 printf(C 级函数)最终写入描述符 1(系统级),但是 printf 在缓冲区未满且 C -string 不包含 \n (这是你的情况)。然后通过调用 _exit 终止将简单地丢失未刷新的缓冲区内容。

在 POSIX 系统上,exit 刷新每个 C 流然后调用 _exit(在真正终止执行之前依次关闭每个系统流)。