In fork() which will run first, parent or child?

2019-02-23 23:57发布

问题:

When the following code runs, I understand the parent and child both will run in parallel immediately after fork() is called.

#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;
}

That means child will execute:

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

And the parent will execute

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

In parallel.

For those not familiar with C, in sh:

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

So I ran this code on a single core cpu and also on dual core, but I noticed that parent is always getting printed first. Since the two are in parallel, it is quite reasonable to expect random order. But it is not happening like that. Why is it like that?

回答1:

Apparently whatever scheduler you're running decides, and it can vary.

I can say from experience that if you assume that one of the two processes always runs first, you will introduce some very subtle race conditions. Either synchronize on something, like a special message on the pipe, or don't assume that either one runs first.



回答2:

I recognise that this might not exactly answer the question, but it may serve to shed some light into what is happening:

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);
    }

After fork has been executed, the parent process continues to run, which then proceeds to the else statement, and executes the printf function. After that it attempts to read from the pipe, but it blocks because there is no data in the pipe. That's right, read() blocks when it attempts to read from a pipe without data.

Documentation that serves to prove this is available. From the xv6 documentation:

If no data is available, a read on a pipe waits for either data to be written or all file descriptors referring to the write end to be closed; in the latter case, read will re- turn 0, just as if the end of a data file had been reached.

And while xv6 may not be Linux, it serves as a design guide to UNIX. If you don't consider this valid enough, then the linux Man pages on pipes can shed some light:

If a process attempts to read from an empty pipe, then read(2) will block until data is available. If a process attempts to write to a full pipe (see below), then write(2) blocks until sufficient data has been read from the pipe to allow the write to complete. Nonblocking I/O is possible by using the fcntl(2) F_SETFL operation to enable the O_NONBLOCK open file status flag.

After that, control is passed to the child, which proceeds to execute its version of the printf() function, write()s to the pipe, and then prints an exiting message, finally exiting after that.

When the child has exited, control is again passed to the parent process, which finds read() on the pipe to be able to read data, which allows it to finish what its work.

So as far as the

In parallel

part is concerned, it doesn't seem to be in parallel exactly. It's serial, it's just very fast for us to notice that it's done in a serial order of execution.



标签: fork