匿名管道
管道是一种进程间通信的形式
头文件:#include< unistd.h >
功能:创建一匿名管道
原型 int pipe(int fd[2])
参数:fd是文件描述符数组,其中fd[0]表示读端,fd[1]表示写端
返回值:成功返回0,失败返回-1,并且设置错误码
上面这张图形象的诠释了什么是管道,其实管道就是内核里的一段内存,给了读端口 和写端口,首先通过一个简单的例子,我们来感受一下管道,并看下怎么用
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
//实现管道
//从屏幕读入数据,写入管道,读取管道,写入屏幕
int Test1(){
int fd[2] = {0};
int ret = pipe(fd);
if (ret < 0){
perror("pipe");
return 1;
}
printf("pipe ok\n");
while (1) {
char buf[1024];
ssize_t read_size = read(0, buf, sizeof(buf) - 1);
if (read_size < 0){
perror("read");
return 1;
}
if (read_size == 0) {
// EOF
printf("read done\n");
return 0;
}
buf[read_size] = '\0';
write(fd[1], buf, strlen(buf));
char out_buf[1024] = {0};
read_size = read(fd[0], out_buf, sizeof(out_buf) - 1);
if (read_size < 0) {
perror("read");
return 1;
}
if (read_size == 0) {
printf("read done\n");
return 0;
}
out_buf[read_size] = '\0';
write(1, out_buf, strlen(out_buf));
}
return 0;
}
运行结果:
可以看出fd[0]就是读端口,fd[1]是写端口
By:
A :2018.5.24 buf[read_size]=0;是为了去掉read从标准输入读出来的\n
B:写进程在管道的尾端写入数据,读进程在管道的首端读出数据。
数据读出后将从管道中移走,其它读进程都不能再读到这些数据。
匿名管道的父子进程通信
原理:父进程创建一个管道 ,之后fork出一个子进程,此时父子进程共享此管道
父子进程实现:
//父子进程通信
//相当于一个读端 一个写端
void Test2() {
int fd[2] = {0};
int ret = pipe(fd);
//成功返回0
//失败返回错误码
if(ret != 0) {
perror("pipe");
return;
}
ret = fork();
if (ret > 0) {
// father用写端
close(fd[0]);
while (1) {
char buf[1024] = {0};
//从屏幕读
ssize_t read_size = read(0, buf, sizeof(buf) - 1);
if (read_size < 0) {
perror("father read");
exit(1);
}
if (read_size == 0) {
printf("father read done\n");
exit(0);
}
buf[read_size] = '\0';
write(fd[1], buf, strlen(buf));
}
} else if (ret == 0) {
// child
close(fd[1]);
while (1) {
char buf[1024] = {0};
ssize_t read_size = read(fd[0], buf, sizeof(buf) - 1);
if (read_size < 0) {
perror("child read");
exit(1);
}
if (read_size == 0) {
printf("child read done\n");
exit(0);
}
buf[read_size] = '\0';
printf("child say: %s", buf);
}
} else {
perror("fork");
return;
}
}
要注意的是上面这个例子父子进程的通信是单向的,若实现全双工可以再创建一个匿名管道
By:父子进程具体谁先调用这个问题或者是否会遭遇阻塞,其实管道是有自己的同步与互斥机制得以保证
管道的读写规则
//光读不写
void Test3() {
int fd[2] = {0};
int ret = pipe(fd);//阻塞
//int ret = pipe2(fd, O_NONBLOCK);//非阻塞
if (ret < 0) {
perror("pipe");
return;
}
printf("pipe ok\n");
char buf[1024] = {0};
ssize_t read_size = read(fd[0], buf, sizeof(buf) - 1);
if (read_size < 0) {
perror("read");
return;
}
printf("read ok\n");
}
//光写不读
void Test4() {
int fd[2] = {0};
//int ret = pipe(fd);//阻塞
int ret = pipe2(fd, O_NONBLOCK);//非阻塞
if (ret < 0) {
perror("pipe");
return;
}
printf("pipe ok\n");
//close(fd[0]);
ssize_t total_size = 0;
while (1) {
char buf[1024] = {0};
ssize_t write_size = write(fd[1], buf, sizeof(buf));
if (write_size < 0) {
perror("write");
return;
}
total_size += write_size;
printf("total_size = %ld\n", total_size);
}
}
//关闭写端
void Test5() {
int fd[2] = {0};
//int ret = pipe(fd);
int ret = pipe2(fd,O_NONBLOCK);
if (ret < 0) {
perror("pipe");
return;
}
printf("pipe ok!\n");
close(fd[1]);
char buf[1024] = {0};
ssize_t read_size = read(fd[0], buf, sizeof(buf) - 1);
if (read_size < 0) {
perror("read");
return;
}
if (read_size == 0) {
printf("read done\n");
return;
}
printf("read ok\n");
}
//关闭读端
void Test6() {
int fd[2] = {0};
int ret = pipe2(fd,O_NONBLOCK);
// int ret = pipe(fd);
if (ret < 0) {
perror("pipe");
return;
}
close(fd[0]);
char buf[1024] = {0};
printf("before write\n");
ssize_t write_size = write(fd[1], buf, sizeof(buf));
printf("after write\n");
if (write_size < 0) {
perror("write");
return;
}
printf("write ok: %ld\n", write_size);
}
总结:
一、阻塞式 pipe
1.关闭对应端口close()
只读不写 read返回等于0 打印出read done
只写不读 当想要写的时候,程序直接停止运行
2.不关闭对应端口
只读不写 发生阻塞
只写不读 阻塞在写 (写了 65536不再写)
二、非阻塞pipe2(,O_NONBLOCK)
1.关闭对应端口close()
只读不写 则read返回等于0 打印出read done
只写不读 当想要写的时候 程序直接停止运行
2.不关闭对应端口
只读不写 read返回小于0 perror打印出错误read: Resource temporarily unavailable
只写不读 写够65536后 当再次写,write返回小于0,perror打印出错误 Resource temporarily unavailable
通过上面的例子我们可以看出
在关闭端口的情况下,阻塞和非阻塞发生状况是一样的,
在不关闭端口的情况下,阻塞和非阻塞的情况就是阻塞和非阻塞特性的最好体现
匿名管道特点:
1.只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行的通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间就可应用该管道。
2.管道面向字节流
3.管道生命周期随进程
4.内核会对管道操作进行同步与互斥
5.管道是半双工的,数据只能向单个方向流动,需要双方通信时,需要建立两个管道