管道的概念

管道是一种最基本的IPC(进程通信)机制,作用于有血缘关系的进程之间,完成数据传递。调用pipe系统函数即可创建一个管道。有如下特质:

1.   其本质是一个伪文件(实为内核缓冲区)

2.   由两个文件描述符引用,一个表示读端,一个表示写端

3.   规定数据从管道的写端流入管道,从读端流出

管道的原理:管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现

管道的局限性:

1.   数据自己读不能自己写

2.   数据一旦被读走,便不在管道中存在,不可反复读取

3.   由于管道采用半双工通信方式。因此,数据只能在一个方向上流动

4.   只能在有公共祖先的进程间使用管道。

 

头文件

#include <unistd.h>

函数原型

int pipe(int pipefd[2]);

调用成功返回0,失败返回-1并设置errno

Description

函数调用成功返回r/w两个文件描述符。无需open, 但需要手动close

规定fd[0]-->r, fd[1]-->w, 就像0 对应标准输入,1对应标准输出一样。

向管道文件读写数据其实就是在读写内核缓冲区。


管道的优劣 

 优点:简单,相比信号,套接字实现进程间通信,简单很多。 

 缺点:

       1. 只能单向通信,双向通信需建立两个管道。          

       2. 只能用于父子、兄弟进程(有共同祖先)间通信。该问题后来使用FIFO命名管道解决。



总结:

 ①读管道

 1. 管道中有数据,read返回实际读到的字节数。      

      2. 管道中无数据:

 (1) 管道写端被全部关闭,read返回0 (好像读到文件结尾)           

(2) 写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)

 ②写管道

1. 管道读端全部被关闭,进程异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)    

         2. 管道读端没有全部关闭:

 (1) 管道已满,write阻塞。         

 (2) 管道未满,write将数据写入,并返回实际写入的字节数。



 

DEMO父子进程间通过管道通信

#include <iostream>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <cstring>

 

using namespace std;

 

int main(){

 

      int fd[2];

      if(pipe(fd) == -1){

           perror("create pipe error");

      }

 

      pid_t pid = fork();

 

      if(pid == -1){

           perror("fork error!");

      }   

 

      if(pid == 0){ // child process read pipe

          

           close(fd[1]);

           char buf[BUFSIZ];

           int len = read(fd[0], buf, sizeof(buf));

           cout << "child process read pipe" << endl;

           cout << "data: " << buf << endl;

           cout << "size: " << len << endl;

           cout << "Finished reading" << endl;

           close(fd[0]);

 

      }else{ // parent process write pipe

 

           close(fd[0]);

           const char * str = "Hello, my friend!";

           cout << "parent process write pipe" << endl;

           cout << "data: " << str << endl;

           cout << "size: " << strlen(str) << endl;

           write(fd[1], str, strlen(str));

           cout << "Finished writing" << endl;

           close(fd[1]);

           sleep(1);

      }

 

      return 0;

}