【问题标题】:Strange output when using fork() in a for loop C在 for 循环 C 中使用 fork() 时的奇怪输出
【发布时间】:2016-07-02 00:31:50
【问题描述】:

我正在玩下面的 C 代码

int main(int argc, char **argv) {

int i;
int n;
int iterations;

if(argc != 2) {
    fprintf(stderr, "Usage: forkloop <iterations>\n");
    exit(1);
}

iterations = atoi(argv[1]);

for(i = 0; i < iterations; i++) {
    n = fork();
    if(n < 0) {
        perror("fork");
        exit(1);
    }
    //printf("pid = %d, i = %d\n", getpid(), i);
    printf("ppid = %d, pid = %d, i = %d\n", getppid(), getpid(), i);
}

return 0;
}

并获得以下输出:

ppid = 16380, pid = 24628, i = 0
ppid = 16380, pid = 24628, i = 1
ppid = 16380, pid = 24628, i = 2
ppid = 24628, pid = 24629, i = 0
ppid = 24628, pid = 24629, i = 1
ppid = 24628, pid = 24629, i = 2
ppid = 16380, pid = 24628, i = 0
ppid = 24628, pid = 24630, i = 1
ppid = 24630, pid = 24635, i = 2

我发现这个输出的第 1 行和第 7 行是相同的,这非常令人困惑。在阅读this post 之后,我得到的印象是每个(子进程和父进程)进程都应该正常运行,因为我希望任何 C 程序都能运行,但程序似乎正在往回跳。这里发生了什么?我现在已经在 2 台不同的机器(运行 Ubuntu 14.04、Ubuntu 12.04 和两者都使用 bash)上进行了尝试,并且在两者上都收到了同样令人困惑的输出。

【问题讨论】:

  • 尝试添加 fflush(stdout);在 printf 调用之后。
  • 这行得通,谢谢!这里的幕后发生了什么?
  • 您发布的程序不是您运行的程序。它不可能在 i=0、i=1 和 i=2 的情况下输出相同数量的行。您发布的程序输出 2x i=0、4x i=1、8x i=2 等。
  • 这是不可能的。也许您在上次编译后对源代码进行了一些更改?即使缓冲导致奇怪,那也会添加输出,而不是删除一些。 i = 2 需要多 6 行。您是否只发布了一些输出? Actual outputsleep 用于防止提示出现在输出中间。)
  • 您是将输出写入文件还是直接写入终端?我强烈怀疑这是printf() anomaly after fork() 的副本。

标签: c linux fork


【解决方案1】:

我不清楚你是如何在输出中得到重复的行的。我的最佳猜测是您遇到了printf() anomaly after fork() 中详述的问题。

我为您的代码创建了一个经过轻微修改的变体。我修改了您的打印代码以打印子 PID(n 的值)以及父进程的信息,并修改了格式,使 PID 始终占用 5 个空格进行对齐。

我添加了代码以接受 -i 3 的迭代次数(默认为 3 次),并接受 -w 以让进程在退出之前等待所有子进程死亡。

代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

int main(int argc, char **argv)
{
    int i;
    int n;
    int waitmode = 0;
    int iterations = 3;

    int opt;
    while ((opt = getopt(argc, argv, "i:w")) != -1)
    {
        switch (opt)
        {
        case 'i':
            iterations = atoi(optarg);
            break;
        case 'w':
            waitmode = 1;
            break;
        default:
            exit(1);
        }
    }

    if (argc != optind)
    {
        fprintf(stderr, "Usage: forkloop [-i iterations] [-w]\n");
        exit(1);
    }

    for (i = 0; i < iterations; i++)
    {
        n = fork();
        if (n < 0)
        {
            perror("fork");
            exit(1);
        }
        // printf("pid = %5d, i = %d\n", getpid(), i);
        printf("ppid = %5d, pid = %5d, i = %d (n = %5d)\n", getppid(), getpid(), i, n);
    }

    if (waitmode)
    {
        int status;
        int corpse;
        while ((corpse = wait(&status)) != -1)
        {
            printf("ppid = %5d, pid = %5d, corpse = %5d, status = 0x%.4X\n",
                    getppid(), getpid(), corpse, status);
        }
    }

    return 0;
}

样本运行

无论好坏,我将程序称为pff19 而不是forkloop。 我正在使用 GCC 6.1.0 在 Mac OS X 10.11.5 上进行测试。该机器具有 Intel Core i7 芯片,因此具有多个内核。当没有等待时,这很重要; PPID 1 表示进程的父进程在孩子报告之前已经死亡。您可以在最后一组输出中看到 PID 15908 首先报告 PPID 15907 然后 PPID 1。

当程序的输出通过cat 进行管道传输时,输出是完全缓冲的,因此子进程在其输出缓冲区中具有其父进程已经打印但尚未刷新的数据 - 这就是'@987654331 @fork()'行为之后的异常。

$ pff19 -i 3 | cat
ppid = 85956, pid = 15878, i = 0 (n = 15880)
ppid = 85956, pid = 15878, i = 1 (n = 15881)
ppid = 85956, pid = 15878, i = 2 (n = 15882)
ppid = 85956, pid = 15878, i = 0 (n = 15880)
ppid = 85956, pid = 15878, i = 1 (n = 15881)
ppid =     1, pid = 15882, i = 2 (n =     0)
ppid = 85956, pid = 15878, i = 0 (n = 15880)
ppid = 15878, pid = 15881, i = 1 (n =     0)
ppid =     1, pid = 15881, i = 2 (n = 15884)
ppid = 15878, pid = 15880, i = 0 (n =     0)
ppid =     1, pid = 15880, i = 1 (n = 15883)
ppid =     1, pid = 15880, i = 2 (n = 15885)
ppid = 85956, pid = 15878, i = 0 (n = 15880)
ppid = 15878, pid = 15881, i = 1 (n =     0)
ppid =     1, pid = 15884, i = 2 (n =     0)
ppid = 15878, pid = 15880, i = 0 (n =     0)
ppid =     1, pid = 15880, i = 1 (n = 15883)
ppid =     1, pid = 15885, i = 2 (n =     0)
ppid = 15878, pid = 15880, i = 0 (n =     0)
ppid = 15880, pid = 15883, i = 1 (n =     0)
ppid =     1, pid = 15883, i = 2 (n = 15886)
ppid = 15878, pid = 15880, i = 0 (n =     0)
ppid = 15880, pid = 15883, i = 1 (n =     0)
ppid = 15883, pid = 15886, i = 2 (n =     0)
$ pff19 -w -i 3 | cat
ppid = 85956, pid = 15888, i = 0 (n = 15890)
ppid = 85956, pid = 15888, i = 1 (n = 15891)
ppid = 15888, pid = 15892, i = 2 (n =     0)
ppid = 85956, pid = 15888, i = 0 (n = 15890)
ppid = 15888, pid = 15891, i = 1 (n =     0)
ppid = 15891, pid = 15895, i = 2 (n =     0)
ppid = 15888, pid = 15890, i = 0 (n =     0)
ppid = 15888, pid = 15890, i = 1 (n = 15893)
ppid = 15890, pid = 15894, i = 2 (n =     0)
ppid = 15888, pid = 15890, i = 0 (n =     0)
ppid = 15890, pid = 15893, i = 1 (n =     0)
ppid = 15893, pid = 15896, i = 2 (n =     0)
ppid = 85956, pid = 15888, i = 0 (n = 15890)
ppid = 15888, pid = 15891, i = 1 (n =     0)
ppid = 15888, pid = 15891, i = 2 (n = 15895)
ppid = 15888, pid = 15891, corpse = 15895, status = 0x0000
ppid = 15888, pid = 15890, i = 0 (n =     0)
ppid = 15890, pid = 15893, i = 1 (n =     0)
ppid = 15890, pid = 15893, i = 2 (n = 15896)
ppid = 15890, pid = 15893, corpse = 15896, status = 0x0000
ppid = 15888, pid = 15890, i = 0 (n =     0)
ppid = 15888, pid = 15890, i = 1 (n = 15893)
ppid = 15888, pid = 15890, i = 2 (n = 15894)
ppid = 15888, pid = 15890, corpse = 15894, status = 0x0000
ppid = 15888, pid = 15890, corpse = 15893, status = 0x0000
ppid = 85956, pid = 15888, i = 0 (n = 15890)
ppid = 85956, pid = 15888, i = 1 (n = 15891)
ppid = 85956, pid = 15888, i = 2 (n = 15892)
ppid = 85956, pid = 15888, corpse = 15892, status = 0x0000
ppid = 85956, pid = 15888, corpse = 15891, status = 0x0000
ppid = 85956, pid = 15888, corpse = 15890, status = 0x0000
$ pff19 -w -i 3 
ppid = 85956, pid = 15898, i = 0 (n = 15899)
ppid = 85956, pid = 15898, i = 1 (n = 15900)
ppid = 15898, pid = 15899, i = 0 (n =     0)
ppid = 85956, pid = 15898, i = 2 (n = 15901)
ppid = 15898, pid = 15899, i = 1 (n = 15902)
ppid = 15898, pid = 15900, i = 1 (n =     0)
ppid = 15898, pid = 15901, i = 2 (n =     0)
ppid = 15898, pid = 15899, i = 2 (n = 15903)
ppid = 15898, pid = 15900, i = 2 (n = 15904)
ppid = 15899, pid = 15902, i = 1 (n =     0)
ppid = 85956, pid = 15898, corpse = 15901, status = 0x0000
ppid = 15899, pid = 15902, i = 2 (n = 15905)
ppid = 15899, pid = 15903, i = 2 (n =     0)
ppid = 15900, pid = 15904, i = 2 (n =     0)
ppid = 15898, pid = 15899, corpse = 15903, status = 0x0000
ppid = 15902, pid = 15905, i = 2 (n =     0)
ppid = 15898, pid = 15900, corpse = 15904, status = 0x0000
ppid = 15899, pid = 15902, corpse = 15905, status = 0x0000
ppid = 85956, pid = 15898, corpse = 15900, status = 0x0000
ppid = 15898, pid = 15899, corpse = 15902, status = 0x0000
ppid = 85956, pid = 15898, corpse = 15899, status = 0x0000
$ pff19 -i 3 
ppid = 85956, pid = 15907, i = 0 (n = 15908)
ppid = 85956, pid = 15907, i = 1 (n = 15909)
ppid = 15907, pid = 15908, i = 0 (n =     0)
ppid = 85956, pid = 15907, i = 2 (n = 15910)
ppid = 15907, pid = 15908, i = 1 (n = 15911)
ppid = 15907, pid = 15909, i = 1 (n =     0)
ppid = 15907, pid = 15910, i = 2 (n =     0)
ppid = 15907, pid = 15909, i = 2 (n = 15913)
ppid =     1, pid = 15908, i = 2 (n = 15912)
ppid = 15908, pid = 15911, i = 1 (n =     0)
ppid =     1, pid = 15913, i = 2 (n =     0)
ppid =     1, pid = 15911, i = 2 (n = 15914)
ppid =     1, pid = 15912, i = 2 (n =     0)
ppid =     1, pid = 15914, i = 2 (n =     0)
$

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-13
    • 1970-01-01
    • 2019-08-05
    • 1970-01-01
    相关资源
    最近更新 更多