fork的输出不正确

4
考虑下面程序的输出结果:
int main()
{
    int ret;
    ret=fork();
    ret=fork();
    ret=fork();
    ret=fork();

    if(!ret)
            printf("one\n");
    else
            printf("two\n");
    return 0;
}

我得到的输出是:
two
one
two
two    

http://ideone.com/omgKm

根据我所知,输出应该是 8 times one8 times two。那么其他的 onetwo 去哪了呢?


1
@Oli:这段代码仍然有点儿怪异。ret只有在结尾处才被检查。 - user195488
@JonathanLeffler:我看到了6个fork失败,4个成功的fork,其中1个是父进程 - 假设子进程不能fork。 - user195488
@JonathanLeffler:在纸上画出来。我正确地认为子进程不能分叉吗?我似乎找不到任何文档来证明它可以。 - user195488
1
@0A0D:子进程绝对可以 fork。 - Oliver Charlesworth
1
@0A0D:Unix 中的每个进程都是原始 PID = 1 的子进程(与其他进程手工制作不同),或者是其子进程之一。您的登录 shell 是一个子进程;它运行子进程,... - Jonathan Leffler
显示剩余8条评论
1个回答

4
考虑下面这个跟踪情况更好的替代代码:
#include <stdio.h>
#include <unistd.h>

int main(void)
{
    int ret1 = fork();
    int ret2 = fork();
    int ret3 = fork();
    int ret4 = fork();

    if (ret4 == 0)
        printf("one: (%d: %d, %d, %d, %d)\n", (int)getpid(), ret1, ret2, ret3, ret4);
    else
        printf("two: (%d: %d, %d, %d, %d)\n", (int)getpid(), ret1, ret2, ret3, ret4);
    return 0;
}

向我们展示这个变化的输出,我们可以看到哪些成功了,哪些失败了。


在看到备选输出后,在我的 Mac 上(其中 Isis JL: 是我的提示符,而 rmkmake 的另一种实现)我得到了这个:

Isis JL: rmk fb && ./fb
    /usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra fb.c -o fb  
two: (38068: 38069, 38070, 38071, 38072)
one: (38072: 38069, 38070, 38071, 0)
two: (38071: 38069, 38070, 0, 38075)
two: (38070: 38069, 0, 38074, 38077)
two: (38073: 0, 0, 38078, 38079)
two: (38069: 0, 38073, 38076, 38080)
one: (38075: 38069, 38070, 0, 0)
one: (38077: 38069, 0, 38074, 0)
Isis JL: two: (38074: 38069, 0, 0, 38081)
two: (38078: 0, 0, 0, 38082)
one: (38079: 0, 0, 38078, 0)
one: (38081: 38069, 0, 0, 0)
two: (38076: 0, 38073, 0, 38083)
one: (38080: 0, 38073, 38076, 0)
one: (38083: 0, 38073, 0, 0)
one: (38082: 0, 0, 0, 0)

Isis JL:

请注意交错的提示——空白行是输出完成后我按回车键的地方。

假设:

Ideone上的输出在初始进程停止后没有被捕获。

尝试使用以下备选方案,在退出之前等待子进程结束:

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

int main(void)
{
    int ret1 = fork();
    int ret2 = fork();
    int ret3 = fork();
    int ret4 = fork();

    if (ret4 == 0)
        printf("one: (%d: %d, %d, %d, %d)\n", (int)getpid(), ret1, ret2, ret3, ret4);
    else
        printf("two: (%d: %d, %d, %d, %d)\n", (int)getpid(), ret1, ret2, ret3, ret4);
    while (wait(0) > 0)
        ;
    return 0;
}

再来说一下在 Mac 上的输出:

Isis JL: rmk fb && ./fb
    /usr/bin/gcc -O3 -g -std=c99 -Wall -Wextra fb.c -o fb  
two: (38111: 38112, 38113, 38114, 38115)
one: (38115: 38112, 38113, 38114, 0)
two: (38114: 38112, 38113, 0, 38119)
two: (38113: 38112, 0, 38117, 38121)
two: (38117: 38112, 0, 0, 38123)
one: (38119: 38112, 38113, 0, 0)
two: (38118: 0, 38116, 0, 38124)
one: (38121: 38112, 0, 38117, 0)
one: (38125: 0, 0, 38122, 0)
two: (38116: 0, 0, 38122, 38125)
two: (38122: 0, 0, 0, 38126)
two: (38112: 0, 38116, 38118, 38120)
one: (38120: 0, 38116, 38118, 0)
one: (38123: 38112, 0, 0, 0)
one: (38124: 0, 38116, 0, 0)
one: (38126: 0, 0, 0, 0)
Isis JL:

假设得到证实

在你可以证明任何事情的范围内... http://ideone.com/zFoLn ...

这显示出所有16行输出。问题必须是“过早终止”导致的。


1
@KurzedMetal:这很奇怪,但有可能我们只是在调试ideone.com;谁知道它是如何收集stdout的。你能在本地环境中重现这个问题吗? - Oliver Charlesworth

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接