进程间通信——管道
1、进程间通信
每个进程有独立的用户地址空间,所以一个进程是看不到另一个进程中的数据包括全局变量。进程间通信是需要通过内核来进行的,内核中开辟一块缓存,不同的进程将数据写到这里或从这里读取,从而实现不同进程之间的通信,内核提供的这种机制称为进程间通信。进程间通信方式有多种,不同的方式提供的共享资源形式不同或者提供者不同。管道就是这其中的一种。
2、管道的创建
管道的创建使用pipe()系统调用,关于此系统调用可参见另一篇文章中的描述——高级IO函数。
3、管道实现进程间通信
管道一般用于有关系的进程之间的通信,例如父子进程之间的通信:
- 父进程创建了一个管道,f[0]、f[1]分别为管道两端的文件描述符
- 父进程通过fork函数创建子进程,此时子进程继承了父进程的管道,也有一对文件描述符f[0]、f[1]。
- 由于管道只能单向传输数据,所以父子进程必须一个关闭f[0],另一个关闭f[1],才能实现通信。如下图所示,即为管道实现进程间通信的抽象描述。
管道实现进程间通信代码示例:
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if(ret == -1)
{
printf("pipe error\n");
return 1;
}
pid_t id = fork();
if(id == 0) //子进程
{
close(fd[1]);
char buf[100];
while(1)
{
memset(buf, '\0', sizeof(buf));
size_t s = read(fd[0], buf, sizeof(buf));
if(s > 0)
{
buf[s - 1] = '\0';
printf("%s", buf);
}
else
break;
}
close(fd[0]);
return 0;
}
else if(id > 0) //父进程
{
close(fd[0]);
char* wrbuf = "hello world";
int i = 5;
while(i--)
{
ret = write(fd[1], wrbuf, strlen(wrbuf));
if(ret < 0)
{
printf("write error\n");
break;
}
sleep(2);
}
close(fd[1]);
int stat_loc;
wait(&stat_loc);
return 0;
}
else
{
printf("fork error\n");
return 1;
}
}
4、管道的读写状态
- 当管道容量满时,且读端文件描述符引用计数大于0,则再次调用write会阻塞;
- 当读端文件描述符引用计数等于0,写端再次调用write时,进程会收到SIGPIPE信号,导致进程终止;
- 当管道数据为空时,且写端文件描述符引用计数大于0,则再次调用read会阻塞,直到有数据可读;
- 当写端文件描述符引用计数等于0,读端再次调用read时,返回0,如同读到文件末尾(EOF)。
5、管道容量
管道有一个容量限制,规定了如果应用程序没有将数据从管道中读走的话,该管道最多能被写入的数据字节数。当前的Linux,管道容量默认为65536字节,也可以使用fcntl函数修改管道容量。
测试管道容量,可以采用对管道只写不读,当write阻塞时说明管道已满,计算write进去的数据字节数,即为管道容量。