所以,我得到了这一行脚本:
echo test | cat | grep test
能否请您给我解释一下究竟如何,将工作给予以下系统调用:管(),叉(),exec()和DUP2()?
我找了一个概述这里主要的操作顺序。 我所知道的,到目前为止是Shell会fork使用fork()和脚本的代码将使用exec更换外壳的一个()。 但是,我们管DUP2? 他们如何在地方跌倒吗?
提前致谢。
所以,我得到了这一行脚本:
echo test | cat | grep test
能否请您给我解释一下究竟如何,将工作给予以下系统调用:管(),叉(),exec()和DUP2()?
我找了一个概述这里主要的操作顺序。 我所知道的,到目前为止是Shell会fork使用fork()和脚本的代码将使用exec更换外壳的一个()。 但是,我们管DUP2? 他们如何在地方跌倒吗?
提前致谢。
首先考虑一个简单的例子,如:
echo test | cat
我们需要的是执行echo
在一个单独的进程,安排它的标准输出要改进程中执行的标准输入cat
。 理论上讲,该引水,一旦安装,就需要由shell没有进一步的干预 - 壳只想平静地等待两个进程退出。
实现这一机制被称为“管道”。 它是在内核中实现,并出口到用户空间进程间通信装置。 一旦通过一个Unix程序创建,管有一对文件描述符用,如果你写入其中之一,你可以阅读从其他的相同数据的特殊属性的外观。 这不是在同一进程中非常有用,但要记住,文件描述符,包括但不限于管道,跨继承fork()
,甚至翻过exec()
这使得管道易于设置和合理有效的IPC机制。
外壳创建管道,现已拥有一套属于管道,一个用于读取,一个用于写入文件描述符。 这些文件描述符被两个分叉子进程继承。 现在,只有当echo
被写入管道的写入终止描述符,而不是其实际的标准输出,如果cat
被从管道的读取结束描述,而不是从标准输入读取,就不会有什么问题。 但他们不这样做,而这正是dup2
的用武之地。
dup2
复制文件描述符作为另一文件描述符,预先自动关闭所述新的描述符。 例如, dup2(1, 15)
将关闭文件描述符1(通过用于标准输出约定),然后重新打开它作为文件描述符15的副本-意味着写入到标准输出将实际上等同于写文件描述符15.这同样适用于阅读: dup2(0, 8)
将使从文件描述符0(标准输入)等效读取从文件描述符8.读取如果我们着手关闭原始文件描述符,打开文件(或管道)将已有效地从原来的描述符到新移动,很像科幻瞬间移动通过在远程位置的第一复制一件事情,然后崩解原来的工作。
如果你还在下面的理论,操作由shell执行的顺序现在应该清楚:
壳创建一个管道,然后fork
两个过程,这两者将继承管文件描述符, r
和w
。
在将要执行的子过程echo
,所述壳调用dup2(1, w); close(w)
dup2(1, w); close(w)
前exec
以重定向标准输出到管道的写入结束。
在将要执行的子过程cat
,壳调用dup2(0, r); close(r)
dup2(0, r); close(r)
以标准输入重定向到管道的读端。
分叉后,主壳过程本身必须关闭管道的两端。 原因之一是释放与管道一旦子过程的出口相关联的资源。 另一种是让cat
来实际终止-管道的读者将获得EOF管道写端的所有副本都关闭之后。 在上面的步骤,我们也关闭了孩子的冗余写入结束的拷贝,文件描述符15,其复制到1。但文件描述符15还必须存在于父之后,因为它是根据这个数字继承,并能仅由父被关闭。 如果不这样做,离开cat
的标准输入从未报告EOF,它的cat
过程中垂下的结果。
这种机制被轻松地将其推广到由管道连接的三个或多个过程。 在三道工序的情况下,管道需要安排该echo
的输出写入到cat
的输入,以及cat
的输出写入到grep
的输入。 这需要两次调用pipe()
三次调用fork()
,四调用dup2()
和close
(一个用于echo
和grep
,两个用于cat
),三次调用exec()
以及四个额外调用close()
(两个用于每个管道)。