管道是一种最基本的IPC(进程间通信:IPC,InterProcess Communication)机制,也称匿名管道,应用于有血缘关系的进程之间,完成数据传递。调用pipe函数即可创建一个管道。
有如下特质:
管道的本质是一块内核缓冲区
由两个文件描述符引用,一个表示读端,一个表示写端。
规定数据从管道的写端流入管道,从读端流出。
当两个进程都终结的时候,管道也自动消失。
管道的读端和写端默认都是阻塞的。
管道的原理
管道的实质是内核缓冲区,内部使用环形队列实现。
默认缓冲区大小为4K,可以使用ulimit -a命令获取大小。
实际操作过程中缓冲区会根据数据压力做适当调整。
管道的局限性
数据一旦被读走,便不在管道中存在,不可反复读取。
数据只能在一个方向上流动,若要实现双向流动,必须使用两个管道
只能在有血缘关系的进程间使用管道。
创建管道-pipe函数
函数作用:
创建一个管道
函数原型:
int pipe(int fd[2]);
函数参数:
若函数调用成功,fd[0]存放管道的读端,fd[1]存放管道的写端
返回值:
成功返回0;
失败返回-1,并设置errno值。
函数调用成功返回读端和写端的文件描述符,其中fd[0]是读端, fd[1]是写端,向管道读写数据是通过使用这两个文件描述符进行的,读写管道的实质是操作内核缓冲区。
创建步骤总结:
父进程调用pipe函数创建管道,得到两个文件描述符fd[0]和fd[1],分别指向管道的读端和写端。
父进程调用fork创建子进程,那么子进程也有两个文件描述符指向同一管。
父进程关闭管道读端,子进程关闭管道写端。父进程可以向管道中写入数据,子进程将管道中的数据读出,这样就实现了父子进程间通信。
//pipe 函数测试
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h> /*for wait*/
int main()
{
//创建管道
//int pipe(int fd[2])
int fd[2];
int ret = pipe(fd);
if(ret < 0)
{
perror("create pipe error");
return ret;
}
//创建子进程
pid_t pid = fork();
if(pid < 0)
{
perror("fork error");
return -1;
}
else if(pid > 0)
{
printf("this is parent:pid == [%d],ppid==[%d]\n",getpid(),getppid());
//关闭读端
close(fd[0]);
write(fd[1],"this is pipe test",strlen("this is pipe test"));
printf("strlen of fd[1]:[%d]\n",strlen("this is pipe test"));
wait(NULL);
}
else
{
printf("this is child :pid == [%d],ppid==[%d]\n",getpid(),getppid());
//关闭写端
close(fd[1]);
char buf[64];
memset(buf,0x00,sizeof(buf));
int n = read(fd[0],buf,sizeof(buf));
printf("read over,n==[%d],buf==[%s]\n",n,buf);
}
return 0;
}
$ gcc pipe.c -o pipe
$ ./pipe
this is parent:pid == [3742],ppid==[2406]
strlen of fd[1]:[17]
this is child :pid == [3743],ppid==[3742]
read over,n==[17],buf==[this is pipe test]