为什么这个程序在运行时使用fork()和wait()输出201?

时间:2015-05-04 23:34:13

标签: c fork wait

当我绘制图表时,我写道可能的输出可能是3020140,1203040,2013040,1302040或3012040.当我运行它时,输出为201.发生了什么?程序怎么可能永远不会达到3,等待条件会发生什么?我认为等待意味着调用过程不会继续,直到子进程完成,这意味着它总是在输出结束时。

int main() {
    if (fork() != 0) {
        if (fork() == 0) {
            printf("3"); 
        }
        else {
            pid_t pid ;
            int status ;
            if ((pid = wait(&status)) > 0) {
                printf("4");            
            } 
        }
    }
    else {
        if (fork() == 0) {
            printf("1");
            exit (0); 
        }
        printf("2"); 
    }
    printf("0");
    return 0; 

}

1 个答案:

答案 0 :(得分:1)

一个观察结果:print语句中完全缺少换行符意味着只有在进程终止时才刷新输出。这使得查看正在发生的事情变得更加困难。因为当进程退出时将刷新字符,所以一个非零数字及其跟随零通常会一起出现。

表面上看,30应该出现在40之前。但是,先死的孩子可能是控制1和20打印的孩子,在这种情况下,40可以出现在打印30之前。请注意,wait()函数在返回之前只等待一个孩子死亡,这与shell命令wait不同,没有参数等待所有孩子死亡。

不保证1的相对排序(后面不会跟0)和20,并且不能保证30,40的排序与1和20相比。如果代码无法打印3,如果在它失败之前的分叉。

当我运行未经修改的代码几次(我称之为程序f7)时,我得到了输出:

$ ./f7;echo
2040
301$ ./f7;echo
3020140
$ ./f7;echo
203040
1$ ./f7;echo
2030140
$ ./f7;echo
2013040
$

这表明测序相当随意。有时,在一些孩子面前完成了回声。

请注意,第一次运行显示40出现在30或1之前;我甚至不必选择我所展示的那些跑步。该机器恰好是运行Ubuntu 14.04衍生产品的基于英特尔的12核心机器,因此有很多内核可供子进程使用。

我要做的第一件事就是添加许多诊断打印(使用换行符)来识别发生在哪里的事情。这意味着每个输出都包含PID。各种父进程将识别其子女的PID; wait将报告报告为死亡的PID。在每次打印操作结束时,使用换行符将检测写入标准错误。输出也与最初的标准输出相呼应。我将此代码保存在f11.c中,因此将其作为./f11运行:

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

static void print(const char *number)
{
    fprintf(stderr, "[%d:%s]\n", (int)getpid(), number);
    printf("%s", number);
}

int main(void)
{
    pid_t pid;
    fprintf(stderr, "[%d:initial]\n", (int)getpid());
    if ((pid = fork()) != 0)
    {
        fprintf(stderr, "[%d:parent-1;child=%d]\n",
                (int)getpid(), (int)pid);
        if ((pid = fork()) == 0)
        {
            print("3");
        }
        else
        {
            fprintf(stderr, "[%d:parent-2;child=%d]\n",
                    (int)getpid(), (int)pid);
            int status;
            if ((pid = wait(&status)) > 0)
            {
                fprintf(stderr, "[%d:corpse=%d;status=0x%.4X]\n",
                        (int)getpid(), (int)pid, status);
                print("4");
            }
        }
    }
    else
    {
        fprintf(stderr, "[%d:child]\n", (int)getpid());
        if ((pid = fork()) == 0)
        {
            print("1");
            exit(0);
        }
        fprintf(stderr, "[%d:parent-3;child=%d]\n", (int)getpid(), (int)pid);
        print("2");
    }
    print("0");
    return 0;
}

示例运行:

$ ./f11;echo
[6781:initial]
[6781:parent-1;child=6782]
[6782:child]
[6781:parent-2;child=6783]
[6782:parent-3;child=6784]
[6782:2]
[6782:0]
[6784:1]
201[6783:3]
[6781:corpse=6782;status=0x0000]
[6781:4]
[6783:0]
[6781:0]
4030
$

如果没有标准错误,输出序列将为2014030。

请注意,我总是看到完整的输出(2030401)。如果你错过了一些输出,可能是因为你的PS1提示覆盖了它(这需要一个不寻常但不是不可能的PS1值,包括回车)。