在 C 中为多个进程之间的进程间通信创建管道

Posted

技术标签:

【中文标题】在 C 中为多个进程之间的进程间通信创建管道【英文标题】:Creating pipes in C for inter process communication between multiple processes 【发布时间】:2020-05-15 11:46:37 【问题描述】:

这个问题已经有多个答案,但没有一个能够帮助我解决我的问题。我正在尝试使用 C 中的匿名管道来理解 IPC。 根据我对管道的理解,它们是一种单向通信通道,具有一个读端和一个写端。 假设我们有两个 c 文件,一个名为 parent.c,另一个名为 child.c。我想要实现的是能够创建 5 个或更多子进程。在此之后,父进程和子进程应该通过标准输入和standard output 与子进程通信,但是由于我希望能够打印父进程从子进程接收到的内容,因此我将使用管道将管道连接到standard error 输出dup2总结 1. 运行一个启动 5 个或更多子进程并运行它们的父程序。 2. 子进程使用scanf等待父进程的输入。 3.父进程向子进程发送消息。 4.子进程收到消息,向父进程发送回复并退出。 5.父进程打印接收到的消息,打印后退出。

parent.c

// Parentc
#include <stdio.h>
#include <stdlib.h>
#include <uinstd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, const char *argv[])
        // File descriptors for the pipes
        int read_pipe[2]; // From child to parent
        int write_pipe[2]; // From parent to child

        pid_t process_id;
        int exit_status;

        // Try to fork 5 child processes
        for(int i = 0; i < 5; i++)
                if(pipe(write_pipe) == -1 || pipe(read_pipe) == -1)
                        perror("Pipe");
                        exit(1);
                

                // Spin a child process
                process_id = fork();

                if(process_id == -1)
                        perror("Fork");
                        exit(1);
                 else if(processId == 0) 
                        // The child process
                        // I don't know what to do here, The idea is to close the
                        // unneeded end of the pipes and wait for input from the parent
                        // process

                         // Start the ./child
                        execl("./child", "");            

                 else 
                        // The parent process
                        char recieved_data[1024];

                        // Send data to child since stderr is duplicated in the pipe
                        // It sends the pid of the child
                         fprintf(stderr, "Test data to %d ", process_id);

                        // Wait to recieve data from child
                        // Don't know how to do that

                         // Print the recieved data
                        printf("Parent recieved: \"%s\"\n", recieved_data);

                        wait(&exit_status); // Will wait till all children exit before exiting
                
        

        return 0;

child.c 是一个如下所示的简单程序

child.c

#include <stdio.h>

int main(int argc, const char *argv[])
        char data_buffer[1024];

        // Wait for input from parent
        scanf("%s", data_buffer);

        // Send data back to parent
        printf("Child process: %s", data_buffer);

        return 0;

预期输出

$ ./parent
parent recived: "Child process: Test data to 12345"
parent recived: "Child process: Test data to 12346"
parent recived: "Child process: Test data to 12347"
parent recived: "Child process: Test data to 12348"
parent recived: "Child process: Test data to 12349"

其中12345、12346....12349是子进程的进程id

【问题讨论】:

你没有使用dup... @Jean-BaptisteYunès 那是因为我不完全理解它是如何工作的。我可以dup2(write_pipe[1], STDIN_FILENO); 但是管道的哪一端要复制,哪一端不要关闭是我不明白的 write_pipe[1] 是管道的写入部分,因此请不要将其重定向到应该读取的内容。 dup2(write[1],STDOUT_FILENO); 可以解决问题,之后写入STDOUT_FILENO 将写入管道。 谢谢。对此,我真的非常感激。经过一些错误,它终于可以正常工作了。 【参考方案1】:

这里有我做的代码,我会用给你解释:

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

int main() 
    char buff[1024];
    int aux, i, count;
    int fds[2], fdss[2];
    pipe(fds); //Here we initialize the file descriptors
    pipe(fdss);
    mode_t fd_mode = S_IRWXU;

    for (i = 0; i < 3; i++) 
        aux = fork();
        if (aux == 0)
            break;
    

    switch (i) 

        case 0:
            printf("Write something:\n");
            scanf("%s[^\n]", buff);
            i = 0;
            count = 0;
            while(buff[i] != '\0') 

                count++;
                i++;
            
            dup2(fds[1], 1);
            close(fds[1]);
            close(fds[0]);
            close(fdss[0]);
            close(fdss[1]);
            write (1, buff, sizeof(buff));
            break;

        case 1:

            dup2(fds[0], 0);
            dup2(fdss[1], 1);
            close(fds[0]);
            close(fds[1]);
            close(fdss[0]);
            close(fdss[1]);
            // 
            if (execl("/bin/grep", "grep", "example", NULL) == -1) 
                printf("Error\n");
                exit (1);
            

            break;

        case 2:
            aux = open("result.txt", O_RDWR | O_CREAT , S_IRWXU);

            dup2(fdss[0], 0);
            dup2(aux, 1);
            close(fds[0]);
            close(fds[1]);
            close(fdss[0]);
            close(fdss[1]);
            close(aux);
            if (execl("/usr/bin/wc", "wc", "-l", NULL) == -1) 
                printf("Error \n");
                exit (1);
            

    
            close(fds[0]);
            close(fds[1]);
            close(fdss[0]);
            close(fdss[1]);
    for (i = 0; i < 3; i++) wait(NULL);

    return 0;

好的,让我们开始吧: 我们使用 pipe() 创建和初始化管道 然后我们编写代码并在 execl() 之前更改文件描述符,以便通过进程传递我们将在控制台中写入的文本,最后在名为 result.txt 的文件中写入“grep example”命令的结果应用于我们所写的文本。 函数 dup2(new_descriptor, old_descriptor) 将新描述符复制到旧描述符中并关闭旧描述符。例如: 在 dup2(fds[1], 1) 之前,我们有: 0 标准输入 1 个标准输出 2 标准错误 在 dup2(fds[1], 1) 之后,我们有: 0 标准输入 1个fds[1] 2 标准错误 注意:如果你不想使用 1,你可以简单地写 STDOUT_FILENO 所以现在我们可以通过进程写入,在我的示例中也可以写入文件

【讨论】:

谢谢@Josep,我会看看并提供反馈 感谢您的代码。这让我有了新的认识。

以上是关于在 C 中为多个进程之间的进程间通信创建管道的主要内容,如果未能解决你的问题,请参考以下文章

第11章进程间通信_管道

swoole进程间如何通信

进程间的通信-队列/管道以及进程间的数据共享和进程池

进程间通信

linux c之通过管道实现兄弟间进程通信:

进程间通信( 管道 )