首页 > 解决方案 > 孩子之间的管道

问题描述

我想做一个程序,首先创建3个进程(A),然后再创建一个进程(B),并且这些第一个进程必须写入管道中,最后一个进程在每次该进程写入时读取。我尝试了一些方法,但我不知道该怎么做,因为进程 (B) 是在进程 (A) 之后创建的

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

#define MAX_CHILDREN 3

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

    int fd[2];

    char buffer[100];

    char str[] = "Hello";
    char str2[] = "Hello2";
    char str3[] = "Hello3";

    for(int num_process = 0; num_process < MAX_CHILDREN; num_process++)
    {
        if(pipe(fd) == -1)
        {
            perror( "pipe Failed" );
            continue;
        }

        pid = fork();

        if(pid < 0)
        {
            perror("fork failed");
            exit(1);
        }

        if(pid == 0)
        { //child code

        if(num_process == 0){
                printf("Child %i (pid= %i) send string %s\n", num_process, getpid(),str);
                write(fd[1],str,strlen(str));
            }
        if(num_process == 1){
                printf("Child %i (pid= %i) send string %s\n", num_process, getpid(),str2);
                write(fd[1],str2,strlen(str2));
        }
        if(num_process == 2){
                printf("Child %i (pid= %i) send string %s\n", num_process, getpid(),str3);
                write(fd[1],str3,strlen(str3));
        }
            exit(0);
        }

        else{//parent
            printf("Im parent %i\n",getpid());
            wait(NULL);
        }
    }

    //Creating another child process from parent, this process recieves string sent from
    //childs
    pid = fork();

        if(pid < 0)
        {
            perror("fork failed");
            exit(1);
        }
    if(pid == 0){//child
    printf("The new process %i read fd pipe\n",getpid());
    if( read(fd[0],buffer,sizeof(buffer)) <= 0) //read pipe
    {
        perror("error read");
        exit( EXIT_FAILURE );
    }
    printf("String readed : %s\n",buffer);
    }
    else{//parent
        wait(NULL);
    }

    return 0;
}

标签: c

解决方案


您需要对代码进行一些更改。父母不应该真正等待它的孩子,直到他们全部启动。由于您为前三个子项中的每一个都创建了一个新管道,因此您需要跟踪正在使用的文件描述符。您应该为此使用数组,以及要发送的字符串。和系统都read()不会write()调用空终止字符串,并且您不会告诉它在末尾写入空字节,因此您需要告诉printf()打印正确的信息。

这些变化和其他各种变化导致:

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

#define MAX_CHILDREN 3

int main(void)
{
    pid_t pid;
    int fd[MAX_CHILDREN][2];
    char buffer[100];
    const char *str[MAX_CHILDREN] = { "Hello 1", "Hello 2", "Hello 3" };

    for (int i = 0; i < MAX_CHILDREN; i++)
    {
        if (pipe(fd[i]) == -1)
        {
            perror("pipe Failed");
            exit(1);
        }
        pid = fork();
        if (pid < 0)
        {
            perror("fork failed");
            exit(1);
        }
        if (pid == 0)
        {
            printf("Child %i (pid= %i) send string %s\n", i + 1, getpid(), str[i]);
            write(fd[i][1], str[i], strlen(str[i]));
            exit(i + 1);
        }
    }

    pid = fork();

    if (pid < 0)
    {
        perror("fork failed");
        exit(1);
    }
    if (pid == 0)
    {
        printf("The new process %i read fd pipe\n", getpid());
        for (int i = MAX_CHILDREN; i-- > 0; )
        {
            int nbytes;
            if ((nbytes = read(fd[i][0], buffer, sizeof(buffer))) <= 0)
            {
                perror("error read");
                exit(EXIT_FAILURE);
            }
            printf("String read: %.*s\n", nbytes, buffer);
        }
        exit(4);
    }

    int corpse;
    int status;
    while ((corpse = wait(&status)) >= 0)
        printf("child %d exited with status 0x%.4X\n", corpse, status);
    return 0;
}

运行时,输出可能是:

Child 1 (pid= 91027) send string Hello 1
Child 2 (pid= 91028) send string Hello 2
Child 3 (pid= 91029) send string Hello 3
The new process 91030 read fd pipe
String read: Hello 3
String read: Hello 2
String read: Hello 1
child 91027 exited with status 0x0100
child 91028 exited with status 0x0200
child 91029 exited with status 0x0300
child 91030 exited with status 0x0400

我颠倒了阅读循环中元素的顺序,主要是为了好玩。for (int i = 0; i < MAX_CHILDREN; i++)如果您愿意,可以改用传统循环。

虽然它在这个程序中并不重要,但您没有在子级或父级中关闭足够多的文件描述符。父级应该关闭管道的写端;它不会使用它们。孩子们应该关闭管道的读取端;他们不会使用它们。此外,第二个和第三个孩子应该关闭为第一个打开的管道,第三个应该关闭第二个的管道,因为他们也不会使用这些。如果您不这样做并且第四个孩子循环等待 EOF(返回 0 字节),它将挂起。

经验法则:如果您 将管道的一端连接到标准输入或标准输出,请 尽快dup2() 关闭返回的两个原始文件描述符 。pipe()特别是,您应该在使用任何 exec*() 函数系列之前关闭它们。

如果您使用 dup() 或 复制描述符,该规则也fcntl() 适用F_DUPFD


请注意,该程序的另一种设计将在循环外创建一个管道,并且孩子们都将写入同一个管道。您可能希望在消息字符串中添加换行符,以便结果是分开的。您肯定会考虑在第四个孩子中循环读取,并且您需要担心管道是否正确关闭,等等。编写代码将是一个值得的子练习。


推荐阅读