第1.3节 管道

作者: 橡树人 | 来源:发表于2020-10-27 22:28 被阅读0次

xv6

第1.3节 管道

这一节中涉及的系统调用有:

  • pipe
  • fork
  • close
  • dup
  • read

一个管道就是一个小型的内核缓冲区,将一对文件描述符暴露给进程:一个用于读,另一个用于写。向管道一端写入的数据可用于另一端读取。
管道提供了一种进程通信的方式。

wc程序的标准输入连接到管道的读取端

int p[2];
char *argv[2];
argv[0] = "wc";
argv[1] = 0;
pipe(p); 
if(fork() == 0) {
      close(0);
      dup(p[0]);
      close(p[0]);
      close(p[1]);
      exec("/bin/wc", argv);
} else {
      close(p[0]);
      write(p[1], "hello world\n", 12);
      close(p[1]);
}

解释:

  • 这个程序调用了系统调用pipe,创建了一个新的管道,使用数组p来记录读文件描述符和写文件描述符。
  • 在执行完fork后,父进程和子进程都持有了指向管道的文件描述符。
  • 子进程调用了closedup使得文件描述符0指向了管道的读取端,关闭关闭在数组p中的文件描述符,调用exec来运行程序wc
  • 当程序wc从标准输入读取数据时,实际上是从管道中读取数据。
  • 父进程关闭管道的读取端,向管道中写入数据,然后关闭管道的写入端。

如果没有数据可用,则对管道发起的read调用要么等着写入数据,要么等着所有引用该写入端的文件描述符关闭。

如果是等着所有引用管道写入端的文件描述符关闭,则read调用就返回0,好像到达文件的末尾一样。

在执行程序wc之前,子进程必须要关闭管道的写入端,理由是read会一直阻塞直到不可能有新数据到达为止。

如果程序wc的文件描述符有一个引用指向管道的写入端,则程序wc会永远看不到输入。

xv6实现诸如grep fork sh.c | wc -l这样的流水线的方式跟前面的代码类似,比如:

case PIPE:
    pcmd = (struct pipecmd*)cmd;
    if(pipe(p) < 0)
      panic("pipe");
    if(fork1() == 0){
      close(1);
      dup(p[1]);
      close(p[0]);
      close(p[1]);
      runcmd(pcmd->left);
    }
    if(fork1() == 0){
      close(0);
      dup(p[0]);
      close(p[0]);
      close(p[1]);
      runcmd(pcmd->right);
    }
    close(p[0]);
    close(p[1]);
    wait(0);
    wait(0);
    break;

子进程创建了一个管道连接到一个流水线的右端。

然后,子进程为流水线的左端调用了forkruncmd,为流水线的右端调用了forkruncmd,并等待两者的完成。

流水线的右端可能是一个命令,该命令自身就包含了一个管道,比如a|b|c,该管道自身派生了两个新进程,一个用于b,另一个用于c

因此,shell可能创建一个进程树。树的叶子是命令,内部节点是等待着左右孩子完成的进程。

从理论上讲,可以让内部节点在流水线的左端运行,但是这样会使得流水线的实现变得复杂。

比如,
假设仅做如下修改:修改sh.c不派生子进程用于p>left,而是在内部进程中运行runcmd(p->left)。然后,比如echo hi | wc将不会产生输出,因为echo hi是以runcmd的方式退出的,内部进程退出了,永远不会调用fork派生子进程来运行管道的右端。
这种错误的行为可通过为了内部进程不以rumcmd的方式调用exit来修正,但是这种修复会使代码变得复杂,因为此时runcmd需要判断其是否是一个内部进程。

当没有派生子进程来runcmd(p->right)时,也会增加复杂性。比如,仅做刚才的修改,sleep 10 | echo hi将会立即输出hi,而不是10秒钟以后输出,因为echo立即运行并退出,并没有等待sleep完成。

由于sh.c的目标是尽可能地简单,所以它不会避免创建内部进程。

注:

管道看起来似乎并没有临时文件功能强大,比如流水线

echo hello world | wc

可被实现成没有管道的形式:

echo hello world >/tmp/xyz; wc </tmp/xyz

管道跟临时文件相比,至少有4点优势:

  1. 管道会自动的清理数据。使用了文件重定向,shell要仔细地移除/tmp/xyz
  2. 管道可传递任意长度的数据流,而文件重定向要求磁盘上有足够的空闲空间来存储所有的数据;
  3. 管道允许并行流水线的状态并行执行,但是文件重定向要求:在第二个程序完成前,第一个程序必须完成;
  4. 如果正在实现进程间通信,则管道的阻塞读和阻塞写回被文件的非阻塞语义更有效。

相关文章

  • 【部署+检查+落实】全力以赴备战“山竹”

    今年第23号“百里嘉”、第22号台风“山竹”来势汹汹,管道公司全体上下积极行动,未雨绸缪。 管道公司...

  • 正在从管道流经的故事

    文/笔芯儿 第56天《让生命微微笑》实修主题:宇宙管道功课:正在从管道流经的故事 每一个人都是宇宙的管道,就像人的...

  • 第1.3节 管道

    xv6 第1.3节 管道 这一节中涉及的系统调用有: pipe fork close dup read 一个管道就...

  • 工业管道工程施工程序

    管道分类与分级 工业管道按压力分级 管道的组成 管道由管道组成件和管道支承件组成 管道组件管道组成件是用于连接或装...

  • 管道基础

    ##管道基础 #通信分类:只写单工管道、只读单工管道、半双工管道(单向读写)、全双工管道(两个半双工管道拼接) 类...

  • Linux 进程间通信

    进程间通信 一 进程间通信 -- 管道 mkfifo test 创建管道文件 匿名管道和命名管道:匿名管道:匿名管...

  • 隧道安全逃生管道最新成果

    新型高分子隧道安全逃生管道分别有高分子逃生管道、新型隧道逃生管道、轻型逃生管道、悬挂式隧道逃生管道、防腐逃生管道、...

  • Linux-C-day-2-进程通过--管道通信

    管道通信 进程间管道通信方式可以通过man 7 pipe来查看; 匿名管道 单工管道 打开管道:使用popen()...

  • 短视频脚本|谈钱说债-8

    短视频脚本|谈钱说债-8 5条现金流管道 赵哥:请问直播财神,你说的5条现金流管道,具体是指什么? 直播财神: 第...

  • Redis管道技术的使用

    目录 Redis 管道技术 SpringDataRedis 使用管道 Redis 管道的性能测试 使用管道技术的注...

网友评论

    本文标题:第1.3节 管道

    本文链接:https://www.haomeiwen.com/subject/mhdfvktx.html