匿名管道通信
原创
1. 管道
什么是管道
管道(pipe)是Unix中最古老的进程间通信的形式,它是一种进程通信(IPC,Inter Process Communication)的机制。如同现实世界的管道,它也有两端,通常在一端写入,另一端读取。从实现来看,它是一个内核的数据缓冲区。
命令行中的管道
在Linux的Shell下,管道是一种强大的特性,用于连接多个命令并实现数据流的传输和处理。它允许将一个命令的输出直接作为另一个命令的输入,从而实现命令之间的协作和数据流水线的构建。
管道的符号是竖线 |
,在命令行中使用它可以将前一个命令的输出作为后一个命令的输入。
cmd1 | cmd2
比如最常见的 grep
命令,使用它时一般需要用管道 |
进行连接。
$ ls / | grep lib
lib
lib32
lib64
2. 匿名管道
创建管道
匿名管道(Anonymous Pipe)是一种在操作系统中用于进程间通信的机制,它是一种临时的、只能在具有亲缘关系的进程之间使用的无名管道。Linux提供了创建匿名管道的接口 pipe
,Linux将管道抽象为文件,一端是只读的,另一端是只写的。
#include <unistd.h>
int pipe(int pipefd[2]);
pipe()
接受一个长度为2的 int
数组作为参数,用于接收管道两端的fd。成功创建管道后,数组中会被填充管道的读端和写端的fd,并返回0。
创建成功后,数组的 pipefd[0]
会被改为管道的读端fd,pipefd[1]
则被改为写端fd。
我们可以尝试将 Hello, world
经过管道传输再输出在屏幕上。
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
int main() {
// 创建管道
int fd[2];
int ret = pipe(fd);
// 向写端写数据
char hello[] = "Hello, world";
write(fd[1], hello, sizeof hello);
// 从读端读数据
char buf[1024];
read(fd[0], buf, sizeof buf);
// 查看
printf("%s\n", buf);
return 0;
}
父子进程通信
在单进程中,使用匿名管道似乎没有什么意义,它更多情况用于进程间的通信。示例代码如下。
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
int main() {
// 创建管道
int fd[2];
assert(pipe(fd) == 0);
// 创建子进程
pid_t pid = fork();
assert(pid >= 0);
if (pid > 0) {
// 父进程关闭写端
close(fd[1]);
// 接收数据
char buf[1024];
while (1) {
int len = read(fd[0], buf, sizeof buf);
buf[len] = '\0';
printf("parent receive: %s\n", buf);
}
} else {
// 子进程关闭读端
close(fd[0]);
// 发送数据
char buf[1024];
while (1) {
printf("child> ");
scanf("%s", buf);
write(fd[1], buf, strlen(buf));
sleep(1); // 每次发完休眠1秒
}
}
return 0;
}
创建匿名管道之后,再使用 fork()
创建子进程,这样子进程中就有了和父进程指向相同管道的fd。
创建子进程后,我们只希望子进程能向父进程发送数据,进行单工通信。于是,父进程关闭不需要的写端,子进程关闭不需要的读端。
执行程序。