进程间通信方式--匿名管道通信

发布时间 2023-04-05 09:42:35作者: nakejimamiyuki

管道的特点

  • 管道其实是一个在内核内存中维护的缓冲器,这个缓冲器的存储能力时有限的,不同的操作系统大小不一定相同。
  • 管道拥有文件的特质:读操作、写操作,匿名管道没有文件实体,有名管道由文件实体,但不存储数据。可以按照操作文件的方式对管道进行操作。
  • 一个管道是一个字节流,试用管道时不存在消息或消息边界的概念,从管道读取数据的进程可以读取任意大小的数据块,而不管写入进程写入管道的数据块的大小是多少。
  • 通过管道传递的数据是顺序的,从管道里读出数据的顺序和它们被写入到管道里的顺序是一致的。
  • 在管道中的数据传输的方向是单向的,一端用于写入,一端用于读取,管道是半双工的。
  • 从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写入更多数据,在管道中无法使用lseek()函数来随机的访问数据。
  • 匿名管道只能在具有公共祖先的进程(父进程与子进程、或者两个兄弟进程、具有亲缘关系)之间使用

匿名管道

  • 管道也叫无名(匿名)管道。它是UNIX系统IPC的最古老形式。所有的UNIX系统都支持这种通信机制。
  • 统计一个目录中文件数目命令:ls | wc -l,为了执行该命令,shell创建了两个进程分别来执行 ls 和 wc。

在Linux终端下输入命令:man 2 pipe查看pipe函数的具体描述:

NAME
       pipe, pipe2 - create pipe

SYNOPSIS
        #include <unistd.h>

        int pipe(int pipefd[2]);
            功能:创建一个匿名管道,用来进程间通信
            参数:
                int pipefd[2]:这个参数是个传出参数
                pipefd[0]:对应的是管道的读端
                pipefd[1]对应的是管道的写端
            返回值:
                成功返回0
                失败返回-1
        
        注意:匿名管道只能用于具有亲缘关系的管道之间。管道默认是阻塞的如果管道中没有数据
        read阻塞,如果管道满了write阻塞。

下面是pipe函数的简单案例:

  1. 父子进程交叉收发信息
  2. 查看管道缓冲大小函数
#include <unistd.h>
#include <sys/types.h>
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
using std::cout;
using std::endl;


namespace jj01{

//子进程发送数据给父进程,父进程读取数据输出
//父子进程交叉收发信息
    void test(){

        //在fork之前创建管道
        int pipefd[2];
        int ret = pipe(pipefd);
        if(ret == -1)
            perror("pipe");


        //创建子进程
        pid_t pid = fork();
        char buf[1024] = {0};
        if(pid > 0){
            //父进程
            //从管道读取数据
            cout << "i am parent,pid: "<<getpid()<<endl;

            
            //不断从管道中读数据
            while(1){
                int leng = read(pipefd[0],buf,sizeof(buf));
                cout << "parent recv : "<<buf<<" pid:"<<getpid()<<endl;

                //管道中写数据
                const char *str = "hello,i am parent";
                write(pipefd[1],str,strlen(str));
                sleep(1);
            }
            
        }
        else if(pid == 0){
            //子进程
            cout << "i am child,pid: "<<getpid()<<endl;
            //不断向管道中写数据
            while(1){
                const char *str = "hello,i am child";
                write(pipefd[1],str,strlen(str));
                sleep(1);

                //从管道中读数据
                int leng = read(pipefd[0],buf,sizeof(buf));
                cout << "child recv : "<<buf<<" pid:"<<getpid()<<endl;
            }

            
        }
    }
        
}

namespace jj02{

    /*
查看管道缓冲大小命令:ulimit -a

查看管道缓冲大小函数
#include <unistd.h>

       long fpathconf(int fd, int name);
       参数:
        int fd:文件描述符
        int name:_PC_PIPE_BUF   获取管道的大小


*/

    void test(){
        int pipefd[2];
        int ret = pipe(pipefd);

        //获取管道的大小
        long psize = fpathconf(pipefd[0],_PC_PIPE_BUF);
        cout <<"管道的大小:"<< psize << endl;

    }
}


int main(){

    //jj01::test();
    jj02::test();
    return 0;
}