1. 无名管道函数接口
通常说的管道通常指无名管道(PIPE)、有名管道(FIFO),但实际上Socket也是管道,这里我们先介绍下无名管道,我们先把无名管道的函数接口列出来
创建无名管道的函数接口规范
功能 | 描述 | 其它 |
---|---|---|
头文件 | #include <unistd.h> | / |
原型 | int pipe(int pipefd[2]); | / |
参数 | pipefd | 一个至少具有2个int型数据的数组,用来存放PIPE的读/写端描述符 |
返回值 | 成功返回0 | 失败返回-1 |
2. 无名管道PIPE特性
先来看PIPE,既然叫管道,那么可以想象它就像一根水管,连接两个进程,一个进程给另一个进程数据,就好像将水灌进管道一样,另一端就可以取出来了。反过来也一样。
先来罗列以下PIPE的特性
- 没有名字,因此无法使用open()
- 只能用于亲缘进程间(如父子进程、兄弟进程、祖孙进程)通信
- 半双工工作方式:PIPE不同于一般文件描述符的的显著之处:它有两个文件描述符,而不是一个!一个只能用来写,另一个只能用来读,这就是所谓的半双工通信方式
- 写入操作不具有原子性,因此只能用来一对一的简单通信的情景
- 不能使用lseek()来定位,因为管道文件不像普通文件那样可以按块的方式存放在硬盘上、Flash等设备上,更像一个看不见源头的水龙头,无法定位
PIPE是一种特殊的文件,虽然它是一种文件,但是却没有名字!因此一般的进程无法通过open()来获取它的文件描述符,它只能在一个进程中被创建出来,然后通过继承的方式将它的文件描述符传递给子进程,这就是为什么PIPE只能用于亲缘进程间通信的原因
3. 无名管道PIPE管道示例
以下代码展示了子进程如何通过PIPE向父进程发送一段数据。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <error.h>
int main(int argc, char **argv){
int fd[2]; //用来存放PIPE的两个文件描述符
if(-1 == pipe(fd)){ //创建PIPE,并将文件描述符存放在fd数组中
perror("pipe()");
exit(1);
}
pid_t pid = fork(); //创建一个子进程,它将会继承PIPE的描述符,fork一次会有两次返回
if(pid < 0){
perror("fork()");
exit(1);
}
if(0 == pid){ //子进程返回时pid == 0
close(fd[0]); //关闭读端
char *s = "Hello World!\n";
write(fd[1], s, strlen(s)); //通过写端fd[1]将数据写入到PIPE中
}
if(pid > 0){ //父进程返回时pid > 0, 此时pid为子进程的进程号
close(fd[1]); //关闭写端
char buff[30];
bzero(buff, 30);
read(fd[0], buff, 30); //通过读端fd[0]将数据从PIPE中读出来
printf("read buffer from child: %s\n", buff);
}
close(fd[0]);
close(fd[1]);
return 0;
}
任何事物的优缺点都是相对的,PIPE简单,但适用场景比较单一、性能较弱