简体   繁体   English

使用 C 中的管道在 ipc 中连续写入和读取消息

[英]Writing and reading messages continuously in ipc with pipes in C

I have written a program where the parent process creates two child processes.我编写了一个程序,其中父进程创建了两个子进程。 The parent process writes to either the first or the second child and the child reads the message.父进程写入第一个或第二个子进程,子进程读取消息。

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

#define MSGSIZE 64

char msgbuf[MSGSIZE];

int main(){

    int p1[2];
    int p2[2];
    int nread;
    int choice = 0;
    pid_t child_a,child_b;


    if(pipe(p1) == -1){
        printf("error in creating pipe\n");
        exit(-1);
    }

    if(pipe(p2) == -1){
        printf("error in creating pipe\n");
        exit(-1);
    }

    child_a = fork();

if (child_a == 0) {
    dup2(p1[0], STDIN_FILENO);
    read(STDIN_FILENO,msgbuf,MSGSIZE); 
    printf("%d receives message: %s\n",getpid(),msgbuf);
    close(p1[0]); 
    close(p1[1]);
} else {
    child_b = fork();

    if (child_b == 0) {
        dup2(p2[0], STDIN_FILENO); 
        read(STDIN_FILENO,msgbuf,MSGSIZE);
        printf("%d receives message: %s\n",getpid(),msgbuf);
        close(p2[0]); 
        close(p2[1]); 
    } else {
        /* Parent Code */
        // Write something to child A
        while(1){
        printf("<child_to_receive_msg> <message>\n");
        scanf("%d %s",&choice,msgbuf);
        switch(choice){
        case 1:
            usleep(250);
            write(p1[1], msgbuf, MSGSIZE);
            break;
        // Write something to child B
        case 2:
            usleep(250);
            write(p2[1], msgbuf, MSGSIZE);
            break;
        case -1: 
            usleep(250);
            printf("parent waiting");
            wait(NULL);
            exit(-1);
            break;
        }
       }
      }
    }

    return 0;
}

My issue is that I want the parent to keep writing to the child process.我的问题是我希望父进程继续写入子进程。 With the above code, once it writes to child or child 2 it wont write again or at least the child process wont read it again.使用上面的代码,一旦它写入 child 或 child 2,它就不会再次写入或至少子进程不会再次读取它。 I don't know if it is possible to do this.我不知道是否有可能做到这一点。

I tried putting the while loop at the beginning of the program but this causes another child process to be created every time.我尝试将 while 循环放在程序的开头,但这会导致每次都创建另一个子进程。

Here my solution, followed by some explanation:这是我的解决方案,然后是一些解释:

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

#define MSGSIZE 64

void read_process(int fh) {

        assert(-1 < fh);

        char msgbuf[MSGSIZE];

        ssize_t retval = 0;

        while (-1 < retval) {
                retval = read(fh, msgbuf, MSGSIZE);
                printf("%d receives message: %s\n", getpid(), msgbuf);
                close(fh);
        }

}

void write_process(int fh_child_1, int fh_child_2) {

        assert(-1 < fh_child_1);
        assert(-1 < fh_child_2);

        char msgbuf[MSGSIZE];
        int choice = -1;

        while (1) {
                printf("<child_to_receive_msg> <message>\n");

                scanf("%d %64s", &choice, msgbuf);

                switch (choice) {
                        case 1:
                                write(fh_child_1, msgbuf, MSGSIZE);
                                break;
                        // Write something to child B
                        case 2:
                                write(fh_child_2, msgbuf, MSGSIZE);
                                break;
                        case -1:
                                printf("parent waiting");
                                wait(NULL);
                                exit(-1);
                                break;
                }
        }
}

int main() {

        /* 0 will be for reading, 1 for writing */
        int p1[2];
        int p2[2];

        pid_t pid;

        if (pipe(p1) == -1) {
                printf("error in creating pipe\n");
                exit(-1);
        }

        /* Don't create 2nd pipe yet, we don't require it here and save us to
         * tear it down in child 1 */

        pid = fork();

        if (pid == 0) {
                close(p1[1]); /* not needed here */
                p1[1] = -1;
                read_process(p1[0]);
                exit(EXIT_SUCCESS);

        } else {
                /* Nobody is going to read from pipe 1 here again */
                close(p1[0]);
                p1[0] = -1; /* Mark fh as invalid */

                if (pipe(p2) == -1) {
                        printf("error in creating pipe\n");
                        exit(-1);
                }

                pid = fork();

                if (pid == 0) {
                        close(p1[1]);
                        p1[1] = -1;

                        close(p2[1]);
                        p2[1] = -1;

                        /* Ensure we did not forget an fh */
                        assert(-1 == p1[0]);
                        assert(-1 == p1[1]);
                        assert(-1 == p2[1]);

                        read_process(p2[0]);
                        exit(EXIT_SUCCESS);

                } else {
                        /* Parent Code */

                        close(p2[0]);
                        p2[0] = -1;

                        assert(-1 == p1[0]);
                        assert(-1 == p2[0]);

                        write_process(p1[1], p2[1]);
                        exit(EXIT_SUCCESS);
                }
        }

        return 0;
}

As noted before, your main problem was that your children processes don't loop but read just once.如前所述,您的主要问题是您的子进程不会循环,而是只读取一次。

There are a few notes, I will first talk about the general ones, and then the more system programming specific ones afterwards.有几个注意事项,我先说一般的,然后再说更多的系统编程特定的。

Your main problem, the missing loops, was hidden below your code being a bit spaghetti.您的主要问题,丢失的循环,隐藏在您的代码之下,有点像意大利面。

C provides means for structuring your code, like functions. C 提供了构建代码的方法,例如函数。 Functions are not only there for re-using code, but can also be used to summarize your code: Instead of pasting the code for your child processes directly where you require it, shift the code to a dedicated function and just call the function.函数不仅用于重用代码,还可以用于总结您的代码:不要将子进程的代码直接粘贴到您需要的地方,而是将代码转移到专用函数并调用该函数。 This aids greatly in understanding the basic structure of the code:这对理解代码的基本结构有很大帮助:

close(p1[1]); /* not needed here */
p1[1] = -1;

read_process(p1[0]);
exit(EXIT_SUCCESS);

is pretty obvious what is the basic idea, isn't it?很明显,基本思想是什么,不是吗? And if you want the gory details of the read process, inspect the read_process function.如果您想了解读取过程的详细信息,请检查read_process函数。

Be careful about your resources.小心你的资源。 Allocate as few as possible, only when you need them.仅在需要时才分配尽可能少的资源。 Free them as soon as possible and mark them as freed - see the pipe file handles.尽快释放它们并将它们标记为已释放 - 请参阅管道文件句柄。

Lastly, if you fork, the process is basically copied.最后,如果你fork,这个过程基本上是复制的。 You create a second process, and it is given copies / clones of all (well, most) of the parent processes resources.您创建了第二个进程,并为它提供了所有(好吧,大多数)父进程资源的副本/克隆

The file handles in your case, are cloned.您的情况下的文件句柄是克隆的。 Eg closing p1[0] in your child does not affect p1[0] in your parent, because they are not the same.例如关闭p1[0]在你的孩子不影响p1[0]在您的父母,因为他们是不一样的。 That also means, that immediately after forking, you should consider all the resources available and get rid of every resource you wont require immediately, like这也意味着,在分叉之后,您应该立即考虑所有可用资源,并立即删除您不需要的所有资源,例如

pid = fork();
if(0 == pid) {
    close(p1[1]); /* not needed here */
    p1[1] = -1;
    read_process(p1[0]);
    exit(EXIT_SUCCESS);
}

Your first child does not require p1[1] , thus close it and mark it as closed and invalid.你的第一个孩子不需要p1[1] ,因此关闭它并将其标记为关闭和无效。

There is probably much more to say, but these are the points that come to my mind immediately.可能还有很多话要说,但这些是我立即想到的要点。

Some of it might not seem clumsy, but as you get more and more experienced, and your code bases grow, you will appreciate these things, at least I do more and more every day.其中一些可能看起来并不笨拙,但是随着您越来越有经验,并且您的代码库不断增长,您会欣赏这些东西,至少我每天做的事情越来越多。 As for the code, there are certainly many bugs still hiding in there, you get the basic idea though I hope ;)至于代码,当然还有很多错误隐藏在那里,尽管我希望你明白基本的想法;)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM