管道(pipe):
常说的管道(pipe)也叫匿名管道,管道的作用于有血缘关系的父子进程或者兄弟进程,通过fork来传递。调用pipe()函数时,会再内核开辟一块缓冲区(称为管道)用于通信,大小为64KB(手动测出来的,只写不读,最多能写65520字节),它有一个读端一个写端,然后通过fd参数传出给用户程序两个文件描述符,fd[0]指向读端,fd[1]指向写端。所以管道在用户程序看起来就像打开一个文件,用过read(fd[0])或write(fd[1]),向这个文件读写数据,其实是在读写内核缓冲区。pipe()函数成功返回0,失败返回-1。
管道特点:
1、他是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端;
2、他只能用于有血缘关系的进程之间的通信,如兄弟进程,父子进程,实现依赖父子进程间资源共享;
3、管道内部有保证数据同步的机制,从而保证访问数据的一致性;
4、他可以看做是一种特殊的文件,对它的读写也可以使用普通的read\write函数。但是他不普通文件,并不属于任何文件系统,并且只存在于内存中;
5、管道随着进程,进程在管道在,进程消失管道对应的端口也关闭,两个进程都消失,管道也消失。
管道实现通信的原理:
1、父进程创建管道,得到两个文件描述符,指向管道两端
2、父进程fork出子进程,子进程也有两个文件描述符指向同一个管道
3、父进程关闭fd[0]读端,子进程关闭fd[1]写端,因为管道只只 支持单向通信。父进程往管道中写,子进程往管道中读,这样就实现了进程间通信
但是有如下几种情况需要注意(前提条件是父子进程都没有退出,提出后相应的fd会被关闭,对应的情况就是fd被关闭的情况):
1、如果有指向管道读端的文件描述符没有关闭(管道读端引用计数大于0),此时有进程通过写端文件描述符向管道内写入数据,若是没有进程从读端读取数据,那么管道被写满后就会被阻塞,直到管道内有空余的位置后才写入数据并返回。
2、如果所有指向管道读端的文件描述符都被关闭(管道读端引用计数为0),此时有进程通过写端文件描述符向管道内些数据时,此时该进程会收到SIGPIPE信号,并异常终止。
3、如果有指向管道写端的文件描述符没有被关闭(管道写端引用计数大于0),而持有管道写端的进程没有向管道内写数据,若是此时有进程从管道读端毒数据,那么读完管道内的剩余数据后就会阻塞等待,直到有数据可读才读取数据返回。
4、如果所有指向管道写端的文件描述符都关闭了(管道写端引用计数为0),而仍然有进程从管道读端读取数据,那么管道内的数据被读完后,再次读取,read就会返回0,就像督导文件结尾。
例子:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if(ret < 0)
{
perror("pipe error \n");
return -1;
}
pid_t pid = fork();
if(pid == 0)
{
int i=0;
close(fd[0]);
char *child="I am child!";
while(i < 10)
{
write(fd[1], child, strlen(child)+1);
i++;
sleep(1);
}
}
else if(pid >0)
{
close(fd[1]);
char msg[100];
int status=0;
int j=0;
while(j<5)
{
memset(msg, 0, sizeof(msg));
ssize_t s = read(fd[0], msg, sizeof(msg));
if(s > 0)
{
msg[s-1] = '\0';
}
printf("%s %d\n", msg j);
}
close(fd[0]);
pid_t ret=waitpid(pid, &status, 0);
printf("exitsingle(%d), exit(%d) \n", status&0xff, (status >> 8)&0xff);
//低8位存放该子进程退出时是否收到信号
//高8位存放子进程退出时的退出码
}
else
{
perror("fork error \n");
return -1;
}
return 0;
}
以上知识点均摘抄自网络,如若侵权,请私信联系删除