在介绍管道之前我们得先了解 什么 是进程间通信,先看一个 视频 缓解一下气氛
一.概念:
在linux环境下 每一个进程都有自己独立的地址空间,彼此之间相互独立,任何一个进程的全局变量在另一个进程中都看不到,所有进程之间不能相互访问,要交换数据必须通过内核(kernal),在内核中开辟一块缓冲区,进程1 把数据拷贝进内核空间,进程2把数据再从 内核空间读走,内核提供的这种机制就是所谓的 进程间通信(IPC)
二.IPC的方式:【后面我都会逐一详细介绍原理和代码实现】
进程间要完成数据的传递要借助操作系统提供的特殊方式完成,如: 文件,管道,内存映射,消息队列,本地套接字,信号,有名管道。随着计算机的发展,一些方式 由于过于落后或者 缺陷逐渐被淘汰,或者弃用,现在通常用的 IPC方式有:
- 管道:使用最简单
- 信号:开销最小(但是很不稳定,而且在linux下,信号机制对线程的支持并不是很好)
- 共享内存:最快(本身是基于内存的读写)
- 本地套接字:最稳定
- 消息队列:可以定义自己的数据格式
- 普通文件读写:开销比较大,不稳定【被淘汰】
三.管道
- 管道又分为两种,匿名管道和有名管道【其实就是一个是有血缘关系的,一个用于没有血缘关系的】。本质就是一个伪文件【实际为内核的缓冲区】
- 由两个文件描述符引用,一个表示读端,一个表示写端。
- 规定数据从管道的写端流入,从读端流出。
- 原理:管道实则是内核使用的环形队列机制,借助内核缓冲区(4k)实现
- 局限性:
- 1.数据自己读不能自己写(除非又两个管道)
- 2.数据一旦被读走,便不在管道之中,不可以反复读取。
- 3.由于管道是半双工的通信方式,因此数据只能单向流动。
四.我们先来介绍管道的第一种,匿名管道
1.创建匿名管道
int pipe(int pipefd[2]), 成功 return 0;失败 return -1,并设置errno
2.使用细节:
1)函数调用成功后,得到 两个pipefd,一个读,一个写,无需open,但是要手动的 close, 规定,pipefd[0] 读端,pipefd[1] 写端和进程的文件描述符类似的。向管道文件读写实质就是 对内核缓冲区的读写。
2)读管道:管道中有数据,read返回实际读到的字节数;如果没有数据并且写端关闭,那就返回0;如果没有数据,但写端没有关闭,那就阻塞等待。
3)写管道:如果读端关闭的,进程异常终止(SIGPIPE【这个信号,还会在网络中触发,当client异常断开的时候】);如果读端打开的,但是pipe这个 管道满了,就等读端把数据读走,腾出空间继续来写,处于阻塞【比较少见】; 如果 没有满 就继续写,返回写入的字节数。
3.代码Demo
#include <iostream>
#include <unistd.h>
#include <string.h>
using namespace std;
#define PIPE_READ 0
#define PIPE_WRITE 1
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t pid = -1;
if (pipe(pipefd) == -1){
perror("creat pipe error");
exit(0);
}else cout << "pipe success" << endl;
pid = fork();
if(pid == 0){// read
char buf[1024];
close(pipefd[PIPE_WRITE]);
ssize_t size = 0;
do {
sleep(1);
size = read(pipefd[PIPE_READ], buf, sizeof(buf));
if(size == 0){
printf("pipe is empty \n");
break;
}
buf[size] = '\0';
cout << "size: " << size << endl;
printf("the text from pipe is: %s\n", buf);
}while(1);
close(pipefd[PIPE_READ]);
}else if(pid > 0){// write
close(pipefd[PIPE_READ]);
ssize_t lenth = 0;
long pipebuf = fpathconf(pipefd[PIPE_WRITE], _PC_PIPE_BUF);
cout << "the pipe buf size is:" << pipebuf << endl; // 4k
sleep(5);
const char* str = "hello world";
do{
lenth = write(pipefd[PIPE_WRITE], str, strlen(str));
}while(1);
close(pipefd[PIPE_WRITE]);
}else{
perror("fork error");
exit(1);
}
return 0;
}
效果: