Linux 之 pipe管道
前言
Linux下有很多IPC机制,pipe管道就是其中的一种。
正文
管道的概念
pipe被称为无名管道,用于亲缘进程间的通信。pipe管道实质为一个内核维护的环形队列,缓冲区大小为4K。工作方式为半双工通信。调用 pipe函数可以创建一个管道。
函数原型:
#include <unistd.h>
int pipe(int fd[2]);
管道有读写两端,fd[0]为读端,fd[1] 为写端。对于一个进程来说,要么对管道进行读操作,要么对其进行写操作,不可以自己写,自己读,所以当一个进程对管道进行写操作之前先关闭其读端,反之亦然。
父写子读
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd[2];
if(pipe(fd) == -1)
{
perror("管道打开失败:");
exit(-1);
}
pid_t id = fork();
if(id == -1)
{
perror("进程创建失败:");
exit(-1);
}
else if(id == 0) //子进程
{
close(fd[1]);
char buf[100];
memset(buf,0,sizeof(buf));
int re = read(fd[0],buf,sizeof(buf));
if(re == 0)
exit(-1);
else
printf("子进程读入成功: %s\n",buf);
}
else // 父进程
{
close(fd[0]);
int re = write(fd[1],"hello world",12);
if(re == 0)
{
printf("父进程写入失败\n");
exit(-1);
}
printf("父进程写入成功\n");
sleep(1);
}
}
输出结果:
父进程写入成功
子进程读入成功: hello world
对于管道的读写:
1.当写端被全部关闭(计数为0),对管道进行读操作,如果其中有数据,会正常读出,直到管道为空,再进行读操作,read返回0。
2.当写端没全部关闭,对管道进行读操作,若管道中无数据,read会阻塞。
3.当读端被全部关闭,读其进行写操作,进程会收到信号SIGPIPE,导致进程异常终止。
4.当读端没被全部关闭,对管道进行写操作,管道缓冲区剩余内存无法将本次内容全部写入,会阻塞到有足够空间才进行写入。
一端写多端读
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd[2];
if(pipe(fd) == -1)
{
perror("管道打开失败:");
exit(-1);
}
pid_t id;
for(int i=0;i<2;i++)
{
id = fork();
if(id == 0)
break;
if(id == -1)
{
perror("进程创建失败:");
exit(-1);
}
}
if(id == 0) //子进程
{
close(fd[1]);
char buf[100];
memset(buf,0,sizeof(buf));
int re = read(fd[0],buf,12);
if(re == 0)
exit(-1);
else
printf("子进程读入成功: %s\n",buf);
}
else // 父进程
{
close(fd[0]);
int re;
for(int i=0;i<2;i++)
{
re = write(fd[1],"hello world",12);
if(re == 0)
{
printf("父进程写入失败\n");
exit(-1);
}
printf("父进程写入成功\n");
}
sleep(1);
}
}
输出结果:
父进程写入成功
父进程写入成功
子进程读入成功: hello world
子进程读入成功: hello world
一端读多端写
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main()
{
int fd[2];
if(pipe(fd) == -1)
{
perror("管道打开失败:");
exit(-1);
}
pid_t id;
for(int i=0;i<2;i++)
{
id = fork();
if(id == 0)
break;
if(id == -1)
{
perror("进程创建失败:");
exit(-1);
}
}
if(id != 0) //父进程
{
close(fd[1]);
char buf[100];
memset(buf,0,sizeof(buf));
for(int i=0;i<2;i++)
{
int re = read(fd[0],buf,12);
if(re == 0)
exit(-1);
else
printf("父进程读入成功: %s\n",buf);
}
}
else // 子进程
{
close(fd[0]);
int re;
re = write(fd[1],"hello world",12);
if(re == 0)
{
printf("子进程写入失败\n");
exit(-1);
}
printf("子进程写入成功\n");
}
}
输出结果:
子进程写入成功
父进程读入成功: hello world
子进程写入成功
父进程读入成功: hello world
管道的限制
1.只能作用于亲缘进程之间。
2.不能自己写自己读。
3.一旦读出后,数据就不会存在于管道中。
4.数据只能在一个方向上流动。