如果 shell 输出通过管道输送到某处,则 fork() 会导致执行父级 main 的开头

fork() causes the beginning of the parent's main to execute if shell output is piped somewhere

我正在尝试使用 fork() 函数,但出现了一些奇怪的行为。在调用 fork() 之后,父进程的主要函数的部分(不是全部)看似 运行,但前提是可执行文件的输出通过管道输送到命令行的某处。

我在 Mac OS Mojave 上注意到了这一点,但我在 Ubuntu 18.04 上测试了相同的代码,它表现出相同的行为。我不知道为什么会这样。

这里是文件中的测试代码fork.c:

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

int main(void)
{
        pid_t child_pid;
        printf("Beginning of main for process %d\n", getpid());
        if ((child_pid = fork())) {
                printf("Forked process %d\n", child_pid);
        } else {
                printf("Beginning of forked process %d\n", getpid());
        }
}

这是一个使用此代码的 shell 会话:

 $ cc fork.c
 $ ./a.out
Beginning of main for process 98373
Forked process 98374
Beginning of forked process 98374
 $ ./a.out | cat
Beginning of main for process 98378
Forked process 98380
Beginning of main for process 98378
Beginning of forked process 98380

正如您在上面看到的,如果输出通过管道传输到某个地方,main() 开头的语句将在父进程中执行两次。

这与 I/O 缓冲有关。

默认情况下,控制台是行缓冲的,所以如果您的输出字符串以换行符结尾并且您没有重定向,输出会立即刷新。但是当您重定向 stdout 时,换行符不会刷新缓冲区。因此,当您分叉父项和子项时,输出缓冲区中都有一行文本,因此两者都打印第一行。

您可以通过使用 fflush.

显式刷新缓冲区来解决此问题
printf("Beginning of main for process %d\n", getpid());
flush(stdout);