C - 通过 execve 传递管道

问题描述 投票:0回答:2

我正在为学校做一个项目,我不确定我尝试解决这个问题的方式是否可行。该项目涉及制作一个程序,分叉 2 个子进程,然后将其 pid 替换为其他程序,并让这 2 个子进程使用 read() 和 write() 通过管道进行通信。

我的问题是使用 execve 并将管道传递给那个孩子。我现在拥有的是这样的:

父程序 - 分叉并让子程序调用 execve:

#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BUF_SIZE 16

/*  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);
    
    int num_dead;

    //forking
    pid_t reader_child_pid;
    pid_t writer_child_pid;
    pid_t child_pid;
    
    //args for each fork
    char *args_1[] = {argv[1], (char *) 0};
    char *args_2[] = {argv[2], (char *) 0};
    
    switch(writer_child_pid = fork()) {
        case -1:
            perror("fork failed");
            return 1;
        case 0:
            close(pfd[1]);
            dup2(pfd[0], 0);
            execve("./writer", args_2, NULL);
            perror("execve failed");
            break;
        default:
            switch(reader_child_pid = fork()) {
                case -1:
                    perror("2nd fork failed");
                    return 1;
                case 0:
                    close(pfd[0]);
                    dup2(pfd[1], 1);
                    execve("./reader", args_1, NULL);
                    perror("execve failed");
                    break;
                default:
                    //close(pfd[0]);
                    //close(pfd[1]);
                    break;
            }
    }
    
    num_dead = 0;
    
    for(;;) {
        if((child_pid=wait(NULL)) == -1){
            if(errno == ECHILD) {
                printf("NO MORE CHILDREN");
                exit(0);
            } else {
                perror("wait error");
                exit(1);
            }
        }
        ++num_dead;
        printf("wait() returned 1 child");
    }
}

尝试使用 dup2 重定向到 stdin 和 stdout。然后在孩子们中,我尝试像这样读取和写入标准输出和标准输入:

子级 - 从标准输入读取数据

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

#define BUF_SIZE 16
int main(int argc, char* argv[]){

    printf("\n\nWRITER\n");
        
        /* ready to process info
        read the file, write it to pipe */
    int wri_inFile = open(argv[0], O_WRONLY | O_CREAT | O_TRUNC | S_IRUSR | S_IWUSR);
    
    char buf[BUF_SIZE];
    int read_test;
    for(;;) {
        read_test = read(0, buf, BUF_SIZE);
        if(read_test == 0) //eof
            break;
        write(wri_inFile, buf, BUF_SIZE);
    }
    
    close(wri_inFile);
    exit(0);
}

我按照您的建议使用 dup,但我不确定我是否按应有的方式访问标准输入和标准输出。

这是另一个孩子仅供参考

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

#define BUF_SIZE 16

int main(int argc, char* argv[]){

    printf("\n\nREADER\n");
    
    /* ready to process info
        read the file, write it to pipe */
    printf("FILE::%s", argv[0]);
    int inFile = open(argv[0], O_RDONLY);
    
    char buf[BUF_SIZE];
    int read_test;
    for(;;) {
        read_test = read(inFile, buf, BUF_SIZE);
        if(read_test == 0) //eof
            break;
        write(1, buf, BUF_SIZE);
    }
    close(argv[0][1]);
    close(inFile);
    printf("\nDONE WITH READER\n");
    exit(0);
}

如果有什么区别,似乎另一个孩子(写入管道的孩子)正在成功写入,但上面的孩子永远不会从管道中读取数据,并且上面的 outFile 始终为空。

****同样的事情仍然发生

我不是在找你帮我解决问题,我只是真的被困住了,不知道我是否做错了什么。再次感谢您的所有帮助。

我正在积极寻找可以使用的示例,但我找不到任何显示他们执行的子级代码的示例,这就是我现在的问题所在。

c exec pipe execve
2个回答
8
投票

使用管道时,必须确保进程没有保持管道打开(除非进程实际上正在使用管道)。特别是,在这种情况下,父进程必须关闭管道的两端,否则读取器将永远不会收到 EOF 指示。

更仔细地阅读其他两个程序后,我发现读取器程序读取给定的文件并将该内容写入管道,写入器程序使用从管道读取的数据写入给定的文件.

此代码是关于可行的最小更改:

#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);
}

请注意,每个子进程和父进程在完成工作之前都会关闭两个管道文件描述符。

我可能不会使用嵌套开关来控制流程,并且我可能会使用函数来处理每个子项的处理:

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...

我还会有一个函数来封装“报告错误并退出”,即使它只有两行长。另外,如果您确实希望来自

printf()
的消息及时显示,请确保它们以换行符结尾。


1
投票

我在下面标记了问题。

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

在这里,在分叉第二个子级之前,您需要关闭父级中管道的两端。因此,第二个子进程的两端也是关闭的。您应该将这些关闭语句移至第二个

default
块,以便在第二个子项分叉后将关闭它们。

switch(reader_child_pid = fork()) {
    // ...
    default:
        switch(writer_child_pid = fork()) {
            // ...
            default:
                close(pfd[1]);
                close(pfd[0]);
                break;
        }
}
© www.soinside.com 2019 - 2024. All rights reserved.