为什么这个简单的代码适用于 `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
(在真正终止执行之前依次关闭每个系统流)。
请看一下这个示例代码,它使用非常成熟的编程模式将 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
(在真正终止执行之前依次关闭每个系统流)。