【问题标题】:Why is this simple code working with `exit` and is not working with `_exit`?为什么这个简单的代码可以使用 `exit` 而不能使用 `_exit`?
【发布时间】:2016-07-02 03:12:43
【问题描述】:

请看一下这个示例代码,它使用非常成熟的编程模式将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] = '\0';
    printf("%s", text);

    return 0;
}

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

【问题讨论】:

  • close(fd[1]) 不,close(fd[0]) 也许?在您的代码中
  • @gsamaras 不是 fd[0] 管道的读取端,我应该在父进程中使用它从管道中读取?
  • 复制后你可以close(fd[1]);,这对我来说似乎更干净一些,但我怀疑这会改变你程序的行为。
  • 致电exit 而不是_exit
  • _exit 可能会或可能不会刷新标准 IO 流(它取决于实现)。 exit 保证刷新标准 IO 流。

标签: c exit


【解决方案1】:

exit() 刷新所有打开的流作为其终止的一部分,而 _exit() 不刷新 - 因此在调用 _exit() 时任何缓冲的输出都会丢失。

你也可以让它“工作”:

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

POSIX 要求流由exit() 刷新:

exit() 函数随后将用未写入的方式刷新所有打开的流 缓冲数据并关闭所有打开的流。最后,该过程应 被终止 [CX] 的后果与在 进程终止的后果。

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

_Exit() [CX] 和 _exit() 函数不得调用函数 在 atexit() 或任何注册的信号处理程序中注册。 [CX] 打开的流不应被冲洗。打开的流是否关闭 (没有刷新)是实现定义的。最后,调用 进程将终止,后果如下所述。

【讨论】:

  • 注意:如果您要产生任何有意义的输出量,禁用 stdout 的缓冲会显着降低性能。真正的解决方案通常是“改为致电exit”; _exit 仅适用于非常有限的用例。
  • @ShadowRanger 在某些情况下_exit() 很有用(例如创建守护进程)或者当您不希望退出处理程序在分叉时运行等时,什么是理想的(exit()或 ``fflush(stdout)` + _exit()) 取决于具体情况。但我同意禁用缓冲是最不受欢迎/最没用的选项(可能是“实时”输出除外——否则,需要重复调​​用 fflush )。
【解决方案2】:

_exit 终止进程而不执行exit(或从main 返回)所做的常规清理步骤。建议在某些情况下在子进程中使用它,因为它可以避免执行不正确或不必要的清理;当派生一个工作人员执行一个短任务时,父任务可能有未刷新的 I/O 缓冲区,并且使用_exit 可以防止父任务和子任务写入相同的缓冲数据,从而神秘地复制输出。它还可以防止执行atexit 处理程序等。

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

【讨论】:

    【解决方案3】:

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

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

    您进行了重定向,让printf(C 级函数)最终写入描述符 1(系统级),但 printf 在缓冲区未满且 C 字符串不包含 @ 时缓冲987654325@(这是你的情况)。然后通过调用_exit 来终止只会丢失未刷新的缓冲区内容。

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

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        猜你喜欢
        • 2019-11-28
        • 1970-01-01
        • 2013-05-04
        • 2012-05-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-08-14
        相关资源
        最近更新 更多