Saturn
Saturn

Reputation: 18149

Waiting for fork children to write to the pipe before continuing

Learning about fork() and pipe() for C.

The below code creates a pipe and a child process. The child sends a message and the parent reads it.

int main () {
    int     fd[2];
    pid_t   pid;
    pipe(fd);
    pid = fork();
    if (pid == 0) {
        // Child
        close(fd[0]);
        write(fd[1], "HELLO", (strlen("HELLO")+1));
        exit(0);
    }else{
        // Parent
        close(fd[1]);
        char buffer[80];
        read(fd[0], buffer, sizeof(buffer));
        printf("Received string: %s", buffer);
    }
    return 0;
}

It works alright. If you comment the write line, the parent will report that it received a string with strange characters, which I guess is normal since it is reading memory that has nothing to do with my program.

But then, it brings on the question: isn't it possible for the parent to read the pipe before the child can write the data? If that were to happen, it would be reading weird characters.

How can the parent "wait" for the data to be written from the child?

In the future I may have several child processes, and I'd like the parent to wait for all children to be "done" with their work before moving on.

Upvotes: 1

Views: 2787

Answers (1)

alk
alk

Reputation: 70901

If you comment the write line, the parent will report that it received a string with strange characters

If the call to write is skipped in the child, the child process ends, the child's writing end of the pipe end gets closed.

The parent detects, that the child's end of the pipe got closed, which makes the call to read() return. The parent actually did not read anything, so the buffer stays uninitialised and "garbage" is printed out.

Please find the relvant section from read's POSIX specification below:

When attempting to read from an empty pipe or FIFO:

  • If no process has the pipe open for writing, read() shall return 0 to indicate end-of-file.

[...]

  • If some process has the pipe open for writing and O_NONBLOCK is clear, read() shall block the calling thread until some data is written or the pipe is closed by all processes that had the pipe open for writing.

To enable your code to handle the case of the child not writing anything, test whether the parent's call to 'read()` really read something:

            ssize_t result = read(fd[0], buffer, sizeof(buffer));
            if (-1 == result) /* Also ALWAYS test for error. */
            {
              perror("read() failed");
            }
            else if (0 == result)
            {
              fprintf(stderr, "read() returned without having read anything.\n")
            }
            else
            {
              printf("Received string: '%s'", buffer);
            }

Upvotes: 1

Related Questions