孩子之间的管道

时间:2018-12-12 01:30:33

标签: c

我想做一个程序,该程序首先创建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;
}

1 个答案:

答案 0 :(得分:2)

您需要对代码进行一些更改。父母不应该真正等孩子们,直到他们全部上马。由于您为前三个子项中的每个子项都创建了一个新管道,因此需要跟踪正在使用的文件描述符。您应该为此使用数组以及要发送的字符串。 read()write()系统都不调用以null结尾的字符串,并且您不要求它在末尾写一个空字节,因此您需要告诉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


请注意,该程序的另一种设计将在循环外部创建单个管道,并且子代将全部写入同一管道。您可能想要在消息字符串中添加换行符,以便将结果分开。您肯定要考虑在第四个子对象中循环读取,并且您需要担心管道已正确关闭,依此类推。对它进行编码将是一个值得的子练习。