在fork()中,它将首先运行,父或子?

时间:2014-02-05 12:25:50

标签: fork

当下面的代码运行时,我理解父和子都将在调用fork()后立即运行并行

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>

int main(void)
{
    int pfds[2];
    char buf[30];

    pipe(pfds);

    if (!fork()) {
        printf(" CHILD: writing to the pipe\n");
        write(pfds[1], "test", 5);
        printf(" CHILD: exiting\n");
        exit(0);
    } else {
        printf("PARENT: reading from pipe\n");
        read(pfds[0], buf, 5);
        printf("PARENT: read \"%s\"\n", buf);
        wait(NULL);
    }

    return 0;
}

这意味着孩子将执行:

printf(" CHILD: writing to the pipe\n"); 

父母将执行

printf("PARENT: reading from pipe\n");

并行。

对于那些不熟悉C的人,请sh

$ sh -c 'echo CHILD & echo PARENT; wait'
PARENT
CHILD

所以我在单核心cpu和双核上运行此代码,但我注意到父母总是首先打印。由于两者并行,因此期望随机顺序是合理的。但事情并非如此。为什么会那样?

2 个答案:

答案 0 :(得分:3)

显然,您正在运行的任何调度程序都会决定,并且它可能会有所不同。

我可以从经验中说,如果你假设两个进程中的一个总是先运行,你会引入一些非常微妙的竞争条件。要么对某些东西进行同步,比如管道上的特殊消息,要么不要假设任何一个先运行。

答案 1 :(得分:2)

我认识到这可能不能完全回答这个问题,但它可能有助于揭示正在发生的事情:

if (!fork()) {
        printf(" CHILD: writing to the pipe\n");
        write(pfds[1], "test", 5);
        printf(" CHILD: exiting\n");
        exit(0);
    } else {
        printf("PARENT: reading from pipe\n");
        read(pfds[0], buf, 5);
        printf("PARENT: read \"%s\"\n", buf);
        wait(NULL);
    }

执行fork之后,父进程继续运行,然后进入else语句,并执行printf函数。之后它会尝试从管道读取,但由于管道中没有数据,它会阻塞。没错,read()在尝试从没有数据的管道中读取时会阻塞。

用于证明此功能的文档可用。来自xv6文档:

  

如果没有可用数据,则管道上的读取将等待数据   写入或所有文件描述符引用写入结束   关闭;在后一种情况下,读取将返回0,就像结束一样   已达到数据文件。

虽然xv6可能不是Linux,但它可以作为UNIX的设计指南。如果您认为这不够有效,那么the linux Man pages on pipes can shed some light

  

如果进程尝试从空管读取,则读(2)将          阻止,直到数据可用。如果进程尝试写入          完整管道(见下文),然后写入(2)块,直到有足够的数据          已从管道中读取以允许写入完成。非阻塞          通过使用fcntl(2)F_SETFL操作来启用I / O.          O_NONBLOCK打开文件状态标志。

之后,控制权传递给孩子,孩子继续执行printf()函数的版本, write()到管道,然后打印退出消息,最后退出。

当孩子退出时,控制权再次传递给父流程,在管道上找到read()以便能够读取数据,这样就可以完成其工作。

至于

  

并行

部分而言,它似乎并不完全平行。这是串行的,我们很快就会注意到它是按顺序执行的。