管道双向通信

Posted

技术标签:

【中文标题】管道双向通信【英文标题】:Two-way communication with pipes 【发布时间】:2020-05-22 13:22:06 【问题描述】:

我需要使用管道在父级和分叉子级之间创建通信。父进程发送0,子进程返回1,父进程回复2,以此类推,直到计数器达到5。然后两个进程都将终止。

我尝试使用两个管道来实现这一点:

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

int main(int argc, char *argv[])

    int fd[2];
    int fd2[2];

    int val = 0;

    // create pipe descriptors
    pipe(fd);
    pipe(fd2);

    // fork() returns 0 for child process, child-pid for parent process.
    if (fork() != 0) 
            ///0. Send: 0
            // parent: writing only, so close read-descriptor.
            close(fd[0]);

            // send the value on the write-descriptor.
            val = 0;
        resend:
            write(fd[1], &val, sizeof(val));
            printf("0.Parent(%d) send value: %d\n", getpid(), val);

            // close the write descriptor
            close(fd[1]);

            ///3. Receive 1;
            close(fd2[1]);

            // now read the data
            read(fd2[0], &val, sizeof(val));
            printf("3.Parent(%d) received value: %d\n", getpid(), val);

            // close the read-descriptor
            close(fd2[0]);
            wait(NULL);
            val++;
            if (val < 6)
                goto resend;
            else
                return 0;

     else    // child: reading only, so close the write-descriptor
        childStart:
        wait(NULL);

        ///1. receive: 0
            close(fd[1]);

            // now read the data (will block)
            read(fd[0], &val, sizeof(val));
            printf("1.Child(%d) received value: %d\n", getpid(), val);

            // close the read-descriptor
            //close(fd[0]);

            ///2. Send: 1
            // parent: writing only, so close read-descriptor.
            close(fd2[0]);

            // send the value on the write-descriptor.
            val = val + 1;
            write(fd2[1], &val, sizeof(val));
            printf("2.Child(%d) send value: %d\n", getpid(), val);

            // close the write descriptor
            close(fd2[1]);
            if(val <5) 
                wait(NULL);
                goto childStart;
            
            else
                return 0;
        
        ///////////////////////////////////////////////////////////////////////////////////

    return 0 ;
    

我得到以下输出:

0.Parent(120978) send value: 0
1.Child(120979) received value: 0
2.Child(120979) send value: 1
1.Child(120979) received value: 1
2.Child(120979) send value: 2
1.Child(120979) received value: 2
2.Child(120979) send value: 3
1.Child(120979) received value: 3
2.Child(120979) send value: 4
1.Child(120979) received value: 4
2.Child(120979) send value: 5
3.Parent(120978) received value: 1
0.Parent(120978) send value: 2
3.Parent(120978) received value: 2
0.Parent(120978) send value: 3
3.Parent(120978) received value: 3
0.Parent(120978) send value: 4
3.Parent(120978) received value: 4
0.Parent(120978) send value: 5
3.Parent(120978) received value: 5

如何让进程等待对方的消息?

【问题讨论】:

resend: if (val &lt; 6) goto resend; 只需使用while 循环...您先使用close(fd[1]); 然后goto resend 并重复使用write(fd[1],。好吧,一旦你关闭了fd[1],你以后就不能再使用它了。 Re "如何让进程等待对方的消息?" 什么意思?您清楚地看到父母在发送下一个号码之前等待回复。孩子也一样 您正在关闭循环中的文件描述符,这会破坏一切;而且您没有检查读/写的退出代码,因此在关闭文件描述符后,每次读/写都是非阻塞的并返回失败,这意味着变量不会被发送或覆盖。 你为什么叫wait在孩子? 【参考方案1】:

你的代码有很多问题,我改了很多东西,结果是这样的:

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

int main(int argc, char *argv[])

    int signo;
    pid_t child_pid,parent_pid;
    sigset_t newmask;
    int fd[2];
    int fd2[2];

    int val = 0;

    // create pipe descriptors
    pipe(fd);
    pipe(fd2);

    // create mask for SIGUSR1
    sigemptyset(&newmask);
    sigaddset(&newmask,SIGUSR1);
    sigaddset(&newmask,SIGUSR2);
    sigprocmask(SIG_BLOCK,&newmask,NULL);

    parent_pid = getpid();

    // fork() returns 0 for child process, child-pid for parent process.
    if ((child_pid = fork()) != 0) 
      // Close unused file descriptors
      close(fd[0]);
      close(fd2[1]);

      // send the value on the write-descriptor.
      for(val = 0; val < 6; val++)
        write(fd[1], &val, sizeof(val));
        printf("0.Parent(%d) send value: %d\n", getpid(), val);
        sigwait(&newmask,&signo);
        kill(parent_pid,SIGUSR2);

        // now read the data
        read(fd2[0], &val, sizeof(val));
        printf("3.Parent(%d) received value: %d\n", getpid(), val);
        sigwait(&newmask,&signo);
        kill(parent_pid,SIGUSR2);
      

      return 0;

     else    // child: reading only, so close the write-descriptor
      // Closing unused file descriptors
      close(fd[1]);
      close(fd2[0]);


      for(; val <= 5; val++)
        // now read the data (will block)
        read(fd[0], &val, sizeof(val));
        printf("1.Child(%d) received value: %d\n", getpid(), val);
        kill(child_pid,SIGUSR1);
        sigwait(&newmask,&signo);

        // send the value on the write-descriptor.
        write(fd2[1], &val, sizeof(val));
        printf("2.Child(%d) send value: %d\n", getpid(), val);
        kill(child_pid,SIGUSR1);
        sigwait(&newmask,&signo);
      

      wait(NULL);

      return 0;
    

你的错误:

您试图在已关闭的文件描述符上写入 您没有在子级和父级之间创建任何类型的同步: 为了创建这个,我使用了 sigwait() (https://www.man7.org/linux/man-pages/man3/sigwait.3.html) 和 kill (https://www.man7.org/linux/man-pages/man2/kill.2.html), 你在孩子和其他没用的地方使用了wait(NULL) 您创建了意大利面条式代码,这不是真正的错误,但不要使用 goto 代替循环。

输出:

0.Parent(14956) send value: 0
1.Child(14957) received value: 0
2.Child(14957) send value: 0
3.Parent(14956) received value: 0
0.Parent(14956) send value: 1
1.Child(14957) received value: 1
2.Child(14957) send value: 1
3.Parent(14956) received value: 1
0.Parent(14956) send value: 2
1.Child(14957) received value: 2
2.Child(14957) send value: 2
3.Parent(14956) received value: 2
0.Parent(14956) send value: 3
1.Child(14957) received value: 3
2.Child(14957) send value: 3
3.Parent(14956) received value: 3
0.Parent(14956) send value: 4
1.Child(14957) received value: 4
2.Child(14957) send value: 4
3.Parent(14956) received value: 4
0.Parent(14956) send value: 5
1.Child(14957) received value: 5
2.Child(14957) send value: 5
3.Parent(14956) received value: 5

【讨论】:

以上是关于管道双向通信的主要内容,如果未能解决你的问题,请参考以下文章

通过匿名管道在进程间双向通信

在 C 中使用管道在父子之间创建双向通信

Qt 通过管道到可执行 Linux 的双向通信

需要检查管道是不是双向工作

高级IO中socketpair实现进程间通信以及重定向

独立程序的多对一双向通信