简体   繁体   中英

Why does program hang on child to parent communication?

I am trying to understand why my program hangs. The Parent sends input froma file it reads to the child program, and the child program will send the result of its computation back to it's parent. However, I have trouble sending the message back through a second pipe. The parent seems to hang when reading from the pipe.

From the other posts, I have read it seems to indicate that the parent should wait for the child to finish by using wait or waitpid (which in my case both of them does not resolve my issue).

I have notice by adding print statement that neither the PARENT or the CHILD finishes.. Could someone please explain to me why this is happening?

Why does this not work?

int main(int argc,char** argv) {
    char buffer[1];
    int i;

    int fd1[2]; int fd2[2];
    pipe(fd1); pipe(fd2);
    pid_t pid;

    // FIRST PROCESS.
    // -------------------
    pid = fork();
    if(pid == 0) {
        int cnt;

        dup2(fd1[0], STDIN_FILENO);
        dup2(fd2[1], STDOUT_FILENO);

        for (i = 0; i < 2; i++) {
            close(fd1[i]);
            close(fd2[i]);
        }

        while(read(STDIN_FILENO, buffer,  sizeof(buffer)) > 0) {
            fprintf(stderr, "( %s )", buffer);
            cnt = cnt + *buffer - 48;
        }

        write(STDOUT_FILENO, &cnt, sizeof(cnt));
        exit(0);
    }


    // PARENT.
    // ------------------------
    int file = open(argv[1], O_RDONLY);

    // READ THE FILE.
    while(read(file, buffer, 1) > 0) {
        if (48 <= *buffer && *buffer <= 57) {
            // PIPE TO CHILD.
            write(fd1[1], buffer, 1);
        } 
    }

    // WAIT FOR CHILD TO FINISH SENDING BACK.

    // int status = 0;
    // waitpid(pid, &status, 0);
    // THIS BLOCK DOESN'T RESOLVE ANYTHING. IT HANGS AT WAIT OR WAITPID.

    // **** THIS IS THE PART WHERE IT DOESN'T WORK.
    while(read(fd2[0], buffer, 1) > 0) {
        fprintf(stderr, "RESULT : %s", buffer);
    }

    // CLOSING PIPES
    for (i = 0; i < 2; i++) {
        close(fd1[i]);
        close(fd2[i]);
    }

    close(file);
    exit(0);
}

You aren't closing enough file descriptors in the parent soon enough.

Rule of thumb : If you dup2() one end of a pipe to standard input or standard output, close both of the original file descriptors returned by pipe() as soon as possible. In particular, you should close them before using any of the exec*() family of functions.

The rule also applies if you duplicate the descriptors with either dup() or fcntl() with F_DUPFD

Now, your child process is following the RoT perfectly. But the corollary for parent processes is that they need to close the unused ends of the pipe, and they must close the write end of a pipe that they use to signal EOF to the reading end of that pipe. This is where your code fails.

Arguably, before reading the file, the parent process should close the read end of the pipe it uses to write to the child, and it should close the write end of the pipe it uses to read from the child.

Then, after reading the whole of the file, it should close the write end of the pipe to the child, before going into the 'read from child' loop. That loop never terminates because the parent still has the write end of the pipe open, so there's a process that could (but won't) write to the pipe.

Also, since the child writes the bytes of an integer onto a pipe, the parent should read the bytes of an integer. Using char buffer[1]; with a %s format is pointless; you need a null terminator for the string, and a single char buffer can't hold both a null byte and any data.

Along with various other improvements ( '0' instead of 48 , for example), you might end up with:

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, char** argv)
{
    if (argc != 2) {
        fprintf(stderr, "Usage: %s filename\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    int fd1[2];
    int fd2[2];
    char buffer[1];

    pipe(fd1);
    pipe(fd2);
    pid_t pid = fork();
    if (pid == 0) {
        int cnt = 0;

        dup2(fd1[0], STDIN_FILENO);
        dup2(fd2[1], STDOUT_FILENO);

        for (int i = 0; i < 2; i++) {
            close(fd1[i]);
            close(fd2[i]);
        }

        while (read(STDIN_FILENO, buffer, sizeof(buffer)) > 0) {
            fprintf(stderr, "(%c)", buffer[0]);      // Changed
            cnt = cnt + buffer[0] - '0';
        }
        putc('\n', stderr);     // Aesthetics

        write(STDOUT_FILENO, &cnt, sizeof(cnt));
        exit(0);
    }

    int file = open(argv[1], O_RDONLY);
    if (file < 0) {
        fprintf(stderr, "failed to open file '%s' for reading\n", argv[1]);
        exit(EXIT_FAILURE);
    }

    close(fd1[0]);      // Added
    close(fd2[1]);      // Added

    while (read(file, buffer, sizeof(buffer)) > 0) {
        if ('0' <= buffer[0] && buffer[0] <= '9') {
            write(fd1[1], buffer, sizeof(buffer));
        }
    }
    close(file);        // Moved
    close(fd1[1]);      // Added

    // Rewritten
    int result;
    while (read(fd2[0], &result, sizeof(result)) == sizeof(result)) {
        fprintf(stderr, "RESULT : %d\n", result);
    }

    close(fd2[0]);      // Added

    // Close loop removed

    return 0;
}

If that is stored in file pipe71.c and compiled, I get the following outputs when it is run:

$ ./pipe71 pipe71.c
(2)(0)(1)(2)(2)(2)(1)(1)(2)(0)(0)(2)(1)(0)(2)(2)(1)(0)(2)(1)(2)(0)(0)(0)(0)(0)(1)(0)(1)(1)(0)(2)(1)(0)(0)(0)(0)(9)(1)(1)(1)(1)(2)(0)(2)(0)(0)
RESULT : 49
$ ./pipe71 pipe71
(0)(0)(8)(0)(0)(2)(2)(0)(8)(1)(1)(5)(1)(1)(1)(1)(5)(1)(1)(1)(8)(5)(1)(9)(8)(5)(1)(1)(0)(4)(4)(4)(6)(0)(2)(8)(0)(0)(0)(2)(7)(1)(3)(8)(3)(0)(4)(3)(0)(4)(9)(0)(0)(0)(0)(7)(1)(9)(8)(1)(3)(0)
RESULT : 178
$

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM