1、管道(pipe)
管道是一种最基本的IPC机制,由pipe函数创建:
#include<unistd.h>
int pipe(int filedes[2]);
调用pipe函数时在内核开辟一块缓存区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(0 输出, 1写入 );
匿名管道的特点:
1> 管道只能进行单向通信;
2> 管道只能用于有血缘关系的通信(父子进程)
3> 管道面向字节流间的一套服务
4> 管道依赖于文件系统,生命周期随进程退出(随进程)
5> 管道不用提供任何保护措施,自主完成了相关的同步,保证了数据的一致性;
下面展示匿名管道的代码:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
int main()
{
int pipefd[2] = {0,0};
if(pipe(pipefd)<0){
perror("pipe");
return 1;
}
pid_t id = fork();
if(id < 0){
perror("fork");
return 2;
}else if(id == 0){//child-> write
close(pipefd[0]); //关闭读端
const char* msg = "Hello bit";
int count = 5;
while(count){
write(pipefd[1], msg, strlen(msg));//write to | 写到管道中
count--;
sleep(1);
}
close(pipefd[1]);
exit(0);
}else{//father-> read
close(pipefd[1]); //关闭写端
int count = 5;
char buf[128];
while(1){
ssize_t _s = read(pipefd[0], buf,sizeof(buf)-1);//read from child
if(_s > 0){
buf[_s] = '\0';
printf("child-> father :%s\n", buf);
}else if(_s == 0){
printf("pipe write is close\n");
break;
}
count--;
}
close(pipefd[0]);
}
}
对于匿名管道注意4个特殊情况:
1. 如果所有指向管道写端的文件描述符都关闭了(管道写端的引用计数等于0),而仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。
2. 如果有指向管道写端的⽂文件描述符没关闭(管道写端的引用计数大于0),而持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。
3. 如果所有指向管道读端的文件描述符都关闭了(管道读端的引用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。
4. 如果有指向管道读端的文件描述符没关闭(管道读端的引用计数大于0),而持有管道读端的 进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。