现在linux使用的IPC(Inter-Process Communication,进程间通信)方式有以下几种:
-(1)管道(pipe)和匿名管道(FIFO)
-(2)信号(signal)
-(3)消息队列
-(4)共享内存
-(5)信号量
-(6)套接字(socket)
管道是Unix中最古老的进程间通信的形式。我们把一个进程连接到另一个进程的一个数据流成为一个“管道”。
只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)进行通信。
#include <unistd.h>
int pipe(int pipefd[2]);
功能:创建无名管道
参数:文件描述符组。fd[0]表示读端,fd[1]表示写端。
返回值:成功返回0,失败返回错误代码
也就是说,在fork()之前pipe(),就可以使得父子进程之间建立起一个管道,画个图:
父子进程都会打开5个文件描述符,除了默认的0、1、2。还有fd[0]、fd[1]。测试一下,就会知道这两个文件描述符为3、4。
写串代码用一用:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd[2];
int retByte;
pid_t pid;
char buf[20] = "";
pipe(fd); /*创建无名管道*/
//printf("%d,%d\n",fd[0],fd[1]);
pid = fork();
if(pid == -1)
{
perror("create fork");
return -1;
}
if(pid == 0)
{
//子进程,写端,使用fd[1]
//close(fd[0]);
//close(fd[1]);
while(1)
{
scanf("%s",buf);
if( write(fd[1],buf,strlen(buf)) == -1)
{
perror("write");
return -1;
}
memset(buf,0,20);
if(read(fd[0],buf,5) > 0 )
{
printf("child-read msg: %s\n",buf);
}
}
}
else
{
//父进程,读端,使用fd[0]
while(1)
{
memset(buf,0,20);
retByte = read(fd[0],buf,5);//每次只读5个
if( retByte == -1)
{
perror("read");
return -1;
}
if(retByte > 0)
{
printf("parent-read msg: %s\n",buf);
}
}
}
return 0;
}
运行结果:
那么,如果没有读端呢?也就是父子进程的fd[0]都关闭了,会有什么现象呢?
void handler(int no)
{
printf("SIGPIPE.\n");
}
int main()
{
int fd[2];
int retByte;
int rlt;
pid_t pid;
char buf[20] = "";
rlt = pipe(fd); /*创建无名管道*/
//printf("%d,%d\n",fd[0],fd[1]);
signal(SIGPIPE,handler);
if(rlt != 0)
{
perror("pipe");
return -1;
}
pid = fork();
if(pid == -1)
{
perror("create fork");
return -1;
}
if(pid == 0)
{
//子进程,写端,使用fd[1]
close(fd[0]);
//close(fd[1]);
while(1)
{
scanf("%s",buf);
if( write(fd[1],buf,strlen(buf)) == -1)
{
perror("write");
return -1;
}
memset(buf,0,20);
}
}
else
{
//父进程,读端,使用fd[0]
close(fd[0]);
while(1)
{
}
}
return 0;
}
运行结果:
会出现管道破裂!!(如果没有重写管道破裂的处理函数,系统默认的处理方式就是杀死进程,父子进程都over了)
所以,总结一下读写规则:
读规则:
1)缓冲区没数据:阻塞
2)缓冲区的数据少于请求字节数:缓冲区有多少就读多少
3)缓冲区的数据多于请求字节数:只读取请求字节数,剩下的还在缓冲区
4)写端关闭:读端等待。
写规则:
1)缓冲区满了:写不进去
2)没有读端:管道破裂,父子进程都结束了。调试到write,发生SIGPIPE。
注意:读端和写端的对应关系可以是一对一、一对多、多对一、多对多的。
上面讲到,缓冲区如果满了,就写不进去了。那么缓冲区有多大呢?换言之,如何检测linux中管道的容量?
代码如下:
int main()
{
int fd[2];
pipe(fd);
char buf[4096]; //4k
int i,loop,ret;
for(i = 0 ; i < sizeof(buf) ; i++)
{
buf[i] = 'a';
}
loop = 100 ; //如果循环结束,还没阻塞,增加循环次数
for(i = 0; i < loop ; i++)
{
ret = write(fd[1],buf,sizeof(buf));
if(ret == -1)
{
perror("write error!\n");
return 1;
}
else
{
printf("write successfully! ");
printf("size: %d K\n", (i+1)*4);
}
}
close(fd[0]);
close(fd[1]);
return 0;
}
运行结果:
在写完64k的时候出现阻塞,说明管道已经满了。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。