Linux Shell中管道的原理及C实现框架

在shell中我们经常用到管道,有没考虑过Shell是怎么实现管道的呢?

Copy
cat minicom.log | grep "error"

标准输入、标准输出与管道#

我们知道,每一个进程都有3个标准的输入输出文件描述符

描述符编号 简介 作用
0 标准输入 通用于获取输入的文件描述符
1 标准输出 通用输出普通信息的文件描述符
2 标准错误 通用输出错误信息的文件描述符

我们还知道,系统调用pipe可以创建无名管道

Copy
int pipe(int pipefd[2]);

pipe的作用是创建无名管道,并创建两个文件描述符

文件描述符 作用
pipefd[0] 管道数据出口
pipefd[1] 管道数据入口

Shell实现管道的原理#

在上文的基础上,我们再看看Shell如何实现管道的。

Shell中通过fork+exec创建子进程来执行命令。如果是含管道的Shell命令,则管道前后的命令分别由不同的进程执行,然后通过管道把两个进程的标准输入输出连接起来,就实现了管道。

例如

Copy
grep "error" minicom.log | awk '{print $1}'

这句命名的作用非常简单,

  1. 通过grep命令在minicom.log中检索含有error关键字的行
  2. 通过awk命令打印grep的输出结果中每一行的第一个字段

在Shell中要实现这样的效果,有4个步骤:

  1. 创建pipe
  2. fork两个子进程执行grep和awk命令
  3. 把grep子进程的标准输出、标准错误重定向到管道数据入口
  4. 把awk子进程的标准输入重定向到管道数据出口

这样就实现了Shell管道:grep把结果输出到管道,awk从管道获取数据

Shell如何用C实现管道#

我没研究过Shell的代码,但不妨碍我们从功能倒推实现,如果是我,我会怎么做呢?

Copy
int main(int argc, char **argv) { while(1) { int pfds[2]; pid_t cmd1, cmd2; if ((cmd1 = fork()) < 0) { ... } else if (cmd1 == 0) { /* child */ /* * dup2 把 pfds[1](管道数据入口描述符) 复制到 文件描述符1&2 * 实现把cmd1的标准输出和标准错误 输送到管道 */ dup2(pfds[1], STDOUT_FILENO); dup2(pfds[1], STDERR_FILENO); close(pfds[0]); close(pfds[1]); exec(cmd1...); __exit(127); } if ((cmd2 = fork()) < 0) { ... } else if (cmd2 == 0) { /* child */ /* * dup2 把 pfds[0](管道数据出口描述符) 复制到 文件描述符0 * 实现cmd2从管道中读取(cmd1的输出)数据 */ dup2(pfds[0], STDIN_FILENO); close(pfds[0]); close(pfds[1]); exec(cmd2...); __exit(127); } close(pfds[0]); close(pfds[1]); wait(...); } }
posted @   广漠飘羽  阅读(2853)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?
点击右上角即可分享
微信分享提示
CONTENTS