fork() 作为循环条件语句

fork() as loop condition statement

我有以下代码,我想解释这段代码中 fork() 调用的行为,中断条件作为命令行参数给出。

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

int main(int argc, char *argv[])
{
    int i = 0;
    char *ptr;
    int x = strtol(argv[1], &ptr, 10);
    while (fork())
    {
        if (i == x)
        {
            break;
        }
        printf("PID: %d, PPID: %d, i = %d\n", getpid(), getppid(), i++);
    }
    return 0;
}

这是当命令行参数为 1 时的输出

PID: 684067, PPID: 14913, i = 0
PID: 684067, PPID: 14913, i = 0

我认为应该发生的是 fork() 调用将执行一次,创建一个子进程和父进程,向父进程返回一个非零进程 ID,向子进程返回一个零。 零值应被解释为 false,只有父进程会继续打印一次,然后将 i 的值递增到 1,然后再次重复创建另一个子进程,该子进程将再次不进入循环并作为 i = 1这次它会跳出循环,那为什么我看到 2 条打印线而不是 1 条? 更高的 i 值的输出更令人费解,但可以预见地打印 i*(i+3)/2 lines。 谁能解释我假设中的缺陷?

您看到的重复消息来自 stdio 缓冲,事实上 printf 默认情况下实际上不打印 - 它只是将一些内容放入缓冲区,稍后将在缓冲区被打印时打印刷新(如果不是在显式刷新或缓冲区填满之前退出,则在进程退出时发生)。

最重要的是,当您派生 child 时,child 会获得所有 parent 状态的副本——包括所有 stdio 缓冲区。因此,如果 stdio 缓冲区中有任何 un-flushed 数据,该数据将被复制并(最终)写入两次——一次由 parent 一次由 child.

您可以通过在 printf 之后放置明确的 fflush(stdout); 来避免这种情况。

使问题复杂化的是,stdio 还支持一种叫做“行缓冲模式”的东西,在这种模式下,每次写入换行符时它都会自动刷新一个 FILE。在某些系统上,这是默认设置在 stdout 上,但在其他系统上不是(它是实现定义的)。您可以通过在 main 函数的开头使用 setlinebuf(stdout); 在 stdout 上显式设置 line-buffered 模式来利用这一点。