【问题标题】:Breaking down shell scripts; What happens under the hood?分解 shell 脚本;引擎盖下会发生什么?
【发布时间】:2013-12-08 00:01:20
【问题描述】:

所以,我得到了这一行脚本:

echo test | cat | grep test

您能否向我解释一下在给定以下系统调用的情况下这将如何工作:pipe()、fork()、exec() 和 dup2()?

我在这里寻找一般概述,主要是操作顺序。 到目前为止我所知道的是,shell 将使用 fork() 进行分叉,脚本的代码将使用 exec() 替换 shell 的代码。但是 pipe 和 dup2 呢?它们是如何到位的?

提前致谢。

【问题讨论】:

  • 请注意,您还需要调用close(),因此您的系统调用列表实际上不足以实现管道

标签: c bash shell unix exec


【解决方案1】:

先考虑一个更简单的例子,比如:

echo test | cat

我们想要的是在一个单独的进程中执行echo,安排它的标准输出被转移到执行cat的进程的标准输入中。理想情况下,这种转移一旦设置完成,就不需要 shell 进一步干预——shell 会平静地等待两个进程退出。

实现这一点的机制称为“管道”。它是在内核中实现并导出到用户空间的进程间通信设备。一旦由 Unix 程序创建,管道就具有一对文件描述符的外观,具有特殊属性,如果您写入其中一个,您可以从另一个读取相同的数据。这在同一进程中不是很有用,但请记住,文件描述符(包括但不限于管道)是跨 fork() 甚至跨 exec() 继承的。这使得管道成为一种易于设置且相当有效的 IPC 机制。

shell 创建管道,现在拥有一组属于管道的文件描述符,一个用于读取,一个用于写入。这些文件描述符由两个分叉的子进程继承。现在只有当echo 正在写入管道的写端描述符而不是其实际的标准输出,并且如果cat 正在从管道的读端描述符而不是其标准输入中读取,一切都会正常工作。但他们没有,这就是dup2 发挥作用的地方。

dup2 将一个文件描述符复制为另一个文件描述符,预先自动关闭新的描述符。例如,dup2(1, 15) 将关闭文件描述符 1(按照用于标准输出的约定),并将其作为文件描述符 15 的副本重新打开——这意味着写入标准输出实际上等同于写入文件描述符 15 . 这同样适用于读取:dup2(0, 8) 将使从文件描述符 0(标准输入)读取等同于从文件描述符 8 读取。如果我们继续关闭原始文件描述符,打开的文件(或管道)将具有已有效地从原始描述符转移到新描述符,就像科幻小说中的传送一样,首先在远程位置复制一块物质,然后分解原始的。

如果你仍然遵循这个理论,那么 shell 执行的操作顺序现在应该很清楚了:

  1. shell创建一个管道,然后fork两个进程,这两个进程都会继承管道文件描述符rw

  2. 在即将执行echo的子进程中,shell在exec之前调用dup2(1, w); close(w),以便将标准输出重定向到管道的写入端。

  3. 在即将执行cat的子进程中,shell调用dup2(0, r); close(r)将标准输入重定向到管道的读取端。

  4. 分叉后,主 shell 进程必须自己关闭管道的两端。一个原因是一旦子进程退出就释放与管道相关的资源。另一种方法是允许cat 实际终止——管道的阅读器只有在管道写入端的所有 个副本都关闭后才会收到EOF。在上面的步骤中,我们确实关闭了子节点的写端冗余副本,文件描述符 15,在其复制为 1 之后。但是文件描述符 15 也必须存在于父节点中,因为它是在该编号下继承的,并且可以只能由父母关闭。如果不这样做,cat 的标准输入永远不会报告 EOF,并且其cat 进程会因此挂起。

这种机制很容易推广到三个或更多通过管道连接的进程。在三个进程的情况下,管道需要安排echo 的输出写入cat 的输入,cat 的输出写入grep 的输入。这需要两次调用pipe(),三次调用fork(),四次调用dup2()close(一次用于echogrep,两次用于cat),三次调用exec() ,以及对close() 的另外四个调用(每个管道两个)。

【讨论】:

  • 在三个进程的情况下不应该有 4 次调用 dup2() 吗?比如调用 1:重定向 echo 输出,调用 2:重定向 cat 输入,调用 3:重定向 cat 输出,调用 4:重定向 grep 输入。
猜你喜欢
  • 1970-01-01
  • 2011-08-02
  • 2019-05-12
  • 2019-04-07
  • 2010-11-03
  • 2023-03-06
  • 2020-01-07
  • 2010-12-14
  • 1970-01-01
相关资源
最近更新 更多