C - Passing A Pipe thru execve

天涯浪子 提交于 2019-12-04 20:17:39
Jonathan Leffler

When using pipes, it is imperative to make sure that the processes are not keeping pipes open (unless the process is actually using the pipe). In particular, in this context, the parent process must close both ends of the pipe because otherwise the reader will never get an EOF indication.

After more careful reading of the other two programs, I see that the reader program reads the file it is given and writes that content to the pipe, and the writer program writes the file it is given with the data that it reads from the pipe.

This code is about the minimum changes that will work:

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

/*  2 cmd line arguments
    1. file already in system (to be copied)
    2. file to be created (the copy)
create pipe (for communication) + 2 child processes
    first child replace pid with reader
    second child with writer
    wait for both children to terminate before exiting
*/

int main(int argc, char* argv[])
{
    //making the pipe
    int pfd[2];
    pipe(pfd);      // Error check omitted!
    int num_dead;

    //forking
    pid_t reader_child_pid;
    pid_t writer_child_pid;
    pid_t child_pid;

    if (argc != 3)
    {
        fprintf(stderr, "Usage: %s infile outfile\n", argv[0]);
        return 1;
    }

    //args for each fork
    char *args_1[] = { "reader", argv[1], (char *) 0 };
    char *args_2[] = { "writer", argv[2], (char *) 0 };

    switch (writer_child_pid = fork()) {
        case -1:
            perror("fork failed");
            return 1;

        case 0:
            // writer reads from standard input (pipe) and writes to named file
            dup2(pfd[0], 0);  // Error check omitted
            close(pfd[0]);
            close(pfd[1]);
            execve("./writer", args_2, NULL);
            perror("execve failed");
            return 1;

        default:
            switch (reader_child_pid = fork()) {
            case -1:
                perror("2nd fork failed");
                return 1;

            case 0:
                //reader reads from the named file and writes to the pipe
                dup2(pfd[1], 1);
                close(pfd[0]);
                close(pfd[1]);
                execve("./reader", args_1, NULL);
                perror("execve failed");
                return 1;

            default:
                // The parent closes both ends of the pipe
                close(pfd[0]);
                close(pfd[1]);
                break;
        }
    }

    num_dead = 0;

    for(;;) {
        if ((child_pid = wait(NULL)) == -1){
            if (errno == ECHILD) {
                printf("NO MORE CHILDREN\n");
                exit(0);
            } else {
                perror("wait error");
                exit(1);
            }
        }
        ++num_dead;
        printf("wait() returned 1 child (%d)\n", (int)child_pid);
    }
    return(0);
}

Note that each child and the parent close both the pipe file descriptors before completing their work.

I would probably not use nested switches for the flow of control, and I would probably use functions to handle the processing of each child:

if ((child1 = fork()) == -1)
    ...error...
else if (child1 == 0)
    launch_reader(pfd, args_1);
else if ((child2 = fork()) == -1)
    ...error...
else if (child2 == 0)
    launch_writer(pfd, args_2);

...parent...

I would also have a function to encapsulate 'report error and exit', even if it was only two lines long. Also, make sure that messages from printf() end with a newline if you actually want them to appear timely.

I marked the problem below.

switch(reader_child_pid = fork()) {
    // ...
    default:
        close(pfd[1]); // <--------------------
        close(pfd[0]);
        switch(writer_child_pid = fork()) {
            // ...
            default:
                break;
        }
}

Here, before forking the second child, you close both ends of the pipe in the parent. Therefore, both ends are closed in the second child process also. You should move those close statements to the second default block so that they will be closed after the second child is forked.

switch(reader_child_pid = fork()) {
    // ...
    default:
        switch(writer_child_pid = fork()) {
            // ...
            default:
                close(pfd[1]);
                close(pfd[0]);
                break;
        }
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!