1、管道简介
管道是Linux提供的进程间通信机制之一,允许通信进程之间通过文件读写的方式单向传递数据。内核实现的文件系统pipefs,会在内核为每个管道文件分配一个的环形缓存区,以支持读/写操作。进程可以使用两种类型的管道进行通信:
- 匿名管道:只支持在父子进程、兄弟进程之间通信。一般使用方式为,父进程调用pipe()创建匿名管道,fork()的子进程默认继承父进程打开的管道
- 命名管道:支持任意的进程通过管道通信。进程通信前,需要先创建fifo类型特殊的文件,然后读写进程分别打开该文件进行读写
2、 管道机制
2.1 匿名使用示例
举一个常见的场景,我们在shell(如bash)执行下面的命令行,该命令行的意思是,将cat 命令执行的标准输出流,重定向至grep命令的输入流。其中'|'字符就是让bash知道,这里有左右两边的命令,需要通过管道进行数据传输。
~ $ cat '/etc/passwd' | grep 'docker'
忽略bash对命令行的解析过程,可以用下面一段简单的代码,展示bash是怎么实现命令之间的管道传输的。
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdlib.h>
int main(int argc, char** argv) {
int fd[2];
// fd[0]为管道写端, fd[1]为管道文件读端
if (pipe(fd) != 0) {
exit(errno);
}
int pid = fork();
if (pid == -1) {
exit(errno);
} else if (pid == 0) {
// 关闭标准输出
close(1);
// 利用dup(fd[1]])从管道写端复制一个描述符,新文件新的文件为最小可用的文件描述符,即上面关闭的1,所以这里是将标准输出重定向至管道写端
dup(fd[1]);
// 关闭管道读端
close(fd[0]);
// 关闭已复制的文件描述符
close(fd[1]);
// 重定向完成,加载命令
execlp("cat", "/etc/passwd", NULL);
} else {
close(0);
dup(fd[0]);
close(fd[1]);
close(fd[0]);
execlp("grep", "docker" , NULL);
}
}
我们知道,Linux进程中文件标识符0、1、2分别代表进程的标准输入、标准输出、标准错误流文件,默认是已打开状态,上面的过程简要介绍如下:
- 10 ~ 13行:进程1调用pipe()命令创建管道文件,文件标识符保存于fd[]
- 15 ~ 17行:进程1调用fork(),创建子进程2,此时两个进程还是执行bash的代码
- 20 ~ 31行:进程1关闭原来的标准输出流,然后将管道的写端,置为新的标准输出流。最后执行execlp函数,加载cat程序并执行(加载后会保留已打开的文件,此时标准输出已被重定向)
- 33 ~ 37行:与上面

本文介绍了Linux中的管道通信机制,包括匿名管道和命名管道的使用。管道是一种单向通信方式,允许进程通过内核的环形缓存区传递数据。文章详细阐述了管道的实现机制,如文件描述符的设置、内核数据结构,并分析了读写操作的过程,强调了SIGPIPE信号的处理和数据原子性的保证。
最低0.47元/天 解锁文章
7186

被折叠的 条评论
为什么被折叠?



