Home > database >  Loop doesn't finish when reading from pipe
Loop doesn't finish when reading from pipe

Time:01-29

I'm following the free book Operating Systems: Three Easy Pieces by Arpaci-Dusseau, and despite being very new to C programming (constructive criticism is welcomed), I tried my luck on coding problem 8 from chapter 5:

Write a program that creates two children, and connects the standard output of one to the standard input of the other, using the pipe() system call.

Here is my attempt (some error-checking is removed for brevity):

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/wait.h>

int main(int argc, char *argv[]) {
    int pipefd[2];
    char buf;

    pipe(pipefd);

    int rc1 = fork();
    if (rc1 == 0) {
        // Child 1, reading from argv, writing to pipe
        close(pipefd[0]);
        write(pipefd[1], argv[1], strlen(argv[1]));
        close(pipefd[1]);
        printf("Child 1 done.\n");
    } else {
        // Parent
        wait(NULL);
        int rc2 = fork();
        if (rc2 == 0) {
            // Child 2, reading from pipe, writing to stdout
            printf("Entering child 2\n");
            close(pipefd[1]);
            while (read(pipefd[0], &buf, 1) > 0) {
                write(STDOUT_FILENO, &buf, 1);
            }
            write(STDOUT_FILENO, "\n", 1);
            printf("Wrote to stdout!\n");
            close(pipefd[0]);
            printf("Child 2 done.\n");
        } else {
            // Still parent
            wait(NULL);
            printf("Parent finished running.\n");
        }
    }
}

which generates the following output, and hangs:

$ ./myprogram "Hello world"
Child 1 done.
Entering child 2
Hello world█

where is the shell cursor, i.e. the while loop hasn't exited to reach the writing of the newline character thereafter.

I did, however, get this to work by replacing char buf; with char buf[1024]; and the lines

while (read(pipefd[0], &buf, 1) > 0) {
    write(STDOUT_FILENO, &buf, 1);
}

with

int n = read(pipefd[0], &buf, 1024);
write(STDOUT_FILENO, &buf, n);

So, now it works. But I don't understand why the previous version did not work. That reading loop was practically identical to the one used in the example at the bottom of the pipe(2) man-pages, which I have verified to be working correctly for me. But why doesn't it work in my own little program?

Possible duplicate questions:
(1) Solution is to close unused ends of pipe. I believe I have done that correctly.
(2) Solution is to break on return codes <= 0, not on < 0. I believe I have done that correctly too.

CodePudding user response:

pipefd[1]) never gets closed in the original parent process. So read(pipefd[0], &buf, 1) will hang in the second child process.

This version

int n = read(pipefd[0], &buf, 1024);
write(STDOUT_FILENO, &buf, n);

doesn't hang because there's no loop. The second child process reads the data written by

write(pipefd[1], argv[1], strlen(argv[1]));

and then continues onward, never checking pipefd[0] again. So it doesn't matter if read(pipefd[0],...) would hang.

  • Related