shinzou
shinzou

Reputation: 6192

Using pipe to pass data between two processes continuously

I'm trying to make two child process to communicate with each other via a pipe.

One child is supposed to keep reading from stdin, passing what it read to the pipe, while the other child is supposed to read from the pipe and print that to stdout.

I manage to get it to work, but only once, in the second iteration after reading a number, I get: write failed: Bad file descriptor.

I can't figure out what am I doing wrong...

#define READ 0
#define WRITE 1
int main(){

    int pipe_descs[2];
    int retval = pipe(pipe_descs);
    int pid = fork();
    if(pid == 0){   //son that reads from stdin and writes to pipe 
        close(pipe_descs[READ]);
        int x;
        while(1){
            open(pipe_descs[WRITE]);
            scanf("%d", &x);
            if(write(pipe_descs[WRITE], &x, sizeof(int)) != sizeof(int)){
                perror("write failed");
                exit(EXIT_FAILURE);
            }
            close(pipe_descs[WRITE]);
        }
        perror("");
    }
    else if(pid > 0){   //parent

        int pid = fork();
        if(pid == 0){   //son that reads from pipe and write to stdout 
            int pipestat;
            int readNum;
            close(pipe_descs[WRITE]);
            while((pipestat = read(pipe_descs[READ], &readNum, sizeof(int))) > 0){

                printf("read: %d \n", readNum);
                //close(pipe_descs[READ]);
                //open(pipe_descs[READ]);
            }
            if (pipestat==-1) {
                perror("");
                fprintf(stderr, "error to pipe... exiting\n");
                exit(EXIT_FAILURE);
            }
        }
    }

    int status;
    int i = 2;
    while (i > 0){
        wait(&status);
        i--;
    }
}

Upvotes: 0

Views: 1926

Answers (1)

ikegami
ikegami

Reputation: 385839

Problem #1

If you're going to close the writer (close(pipe_descs[WRITE]);), you can't keep using it! Either remove that statement, or exit the loop after closing the pipe.

Problem #2

open(pipe_descs[WRITE]) makes no sense! Get rid of that.

Problem #3

[If your system complies with POSIX.1-2001, and if the amount you are writing is smaller than PIPE_BUF, then you can ignore this section.]

write(..., sizeof(int)) != sizeof(int) doesn't (necessarily) indicate an error condition. If no error occurred, then write didn't set errno to something meaningful, so the error message you obtained is meaningless.

Since it's not an error for write to write less than the requested amount, it needs to be called in the a loop. Replace

        if(write(pipe_descs[WRITE], &x, sizeof(int)) != sizeof(int)){
            perror("write failed");
            exit(EXIT_FAILURE);
        }

with

        my_write(pipe_descs[WRITE], &x, sizeof(x));

and add

void my_write(int fd, const void *buf, size_t bytes_to_write) {
    while (bytes_to_write > 0) {
        ssize_t bytes_written = write(fd, buf, bytes_to_write);
        if (bytes_written == -1) {
            perror("write failed");
            exit(EXIT_FAILURE);
        }

        buf            += bytes_written;
        bytes_to_write -= bytes_written;
    }
}

Similar, it's not an error for read to return less than the requested amount. As such, it also needs to be called in the a loop. Replace

        while((pipestat = read(pipe_descs[READ], &readNum, sizeof(int))) > 0){

            printf("read: %d \n", readNum);
            //close(pipe_descs[READ]);
            //open(pipe_descs[READ]);
        }
        if (pipestat==-1) {
            perror("");
            fprintf(stderr, "error to pipe... exiting\n");
            exit(EXIT_FAILURE);
        }

with

        while (my_read(pipe_descs[READ], &readNum, sizeof(readNum))) {
            printf("read: %d \n", readNum);
        }

and add

int my_read(int fd, void *buf, size_t bytes_to_read) {
    int has_read = 0;
    while (bytes_to_read > 0) {
        ssize_t bytes_read = read(fd, buf, bytes_to_read);
        if (bytes_read == -1) {
            perror("read failed");
            exit(EXIT_FAILURE);
        }

        if (bytes_read == 0) {
             if (has_read) {
                 fprintf(stderr, "read failed: Premature EOF");
                 exit(EXIT_FAILURE);
             }

             return 0;
        }

        has_read = 1;

        buf           += bytes_read;
        bytes_to_read -= bytes_read;
    }

    return 1;
}

Upvotes: 2

Related Questions