【问题标题】:Writing and reading messages continuously in ipc with pipes in C使用 C 中的管道在 ipc 中连续写入和读取消息
【发布时间】:2020-03-27 23:48:18
【问题描述】:

我编写了一个程序,其中父进程创建了两个子进程。 父进程写入第一个或第二个子进程,而子进程读取消息。

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

我的问题是我希望父进程继续写入子进程。使用上面的代码,一旦它写入子进程或子进程 2,它就不会再次写入,或者至少子进程不会再次读取它。我不知道是否可以这样做。

我尝试将 while 循环放在程序的开头,但这会导致每次都创建另一个子进程。

【问题讨论】:

  • 父进程正在执行多次写入,但子进程仅执行一次读取。子进程代码中也需要循环。
  • 为什么在整个代码中都有usleep 调用?应该不需要。
  • 为什么要复制文件句柄?恕我直言,您无需复制文件句柄,无论如何,这些句柄都是在 fork 上“复制”的(我认为是引用)。只需使用它们在每个子项中读取或写入并关闭它们而无需重复。

标签: c linux pipe fork ipc


【解决方案1】:

这是我的解决方案,然后是一些解释:

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

如前所述,您的主要问题是您的子进程不会循环,而是只读取一次。

有几点注意事项,我先讲一般的,后面再讲更多系统编程的具体的。

您的主要问题,即缺少的循环,隐藏在您的代码下方,有点意大利面。

C 提供了结构化代码的方法,例如函数。 函数不仅用于重用代码,还可以用于总结代码:与其将子进程的代码直接粘贴到您需要的地方,不如将代码转移到专用函数并调用该函数。这对理解代码的基本结构有很大帮助:

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

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

很明显,基本思想是什么,不是吗?如果您想了解读取过程的血腥细节,请检查 read_process 函数。

小心你的资源。 尽可能少地分配,仅在需要时分配。尽快释放它们并将它们标记为已释放 - 查看管道文件句柄。

最后,如果你fork,这个过程基本上是复制的。您创建第二个进程,并为其提供所有(嗯,大部分)父进程资源的副本/克隆

在您的情况下,文件句柄是克隆的。 例如。在您的孩子中关闭p1[0] 不会影响您父母中的p1[0],因为它们不一样。 这也意味着,在分叉后,您应该立即考虑所有可用资源并摆脱您不会立即需要的所有资源,例如

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

您的第一个孩子不需要p1[1],因此请将其关闭并将其标记为已关闭且无效。

可能还有很多话要说,但这些是我立即想到的要点。

其中一些可能看起来并不笨拙,但是随着您越来越有经验,并且您的代码库不断增长,您会欣赏这些东西,至少我每天做的越来越多。 至于代码,肯定还有很多bug隐藏在那里,不过我希望你能明白基本的想法;)

【讨论】:

  • 谢谢,但这对我没有帮助,实际上它离解决方案更远了。
  • 那么,如果你同时有更好的解决方案,在这里分享一下就好了?
猜你喜欢
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多