管道如何在Linux中工作


25

我一直在阅读有关如何在Linux内核中实现管道的信息,并希望验证我的理解。如果我不正确,将选择带有正确解释的答案。

  • Linux有一个称为pipefs的VFS,它安装在内核中(而不是在用户空间中)
  • pipefs有一个超级块,并安装在它自己的根目录(pipe:)旁边/
  • 与大多数文件系统不同,无法直接查看pipefs
  • pipefs的条目是通过pipe(2)syscall进行的
  • pipe(2)外壳程序使用的syscall与|操作员进行管道传输(或从任何其他进程手动进行管道传输)在pipefs中创建一个新文件,其行为与普通文件非常相似
  • 管道运算符左侧的文件已stdout重定向到pipefs中创建的临时文件
  • 管道运算符右侧的文件已stdin设置为pipefs上的文件
  • pipefs存储在内存中,并且通过一些内核魔术,不应进行分页

这种关于管道(例如ls -la | less)功能如何的解释是否正确?

我不明白的一件事是,诸如bash之类的东西将如何设置进程stdinstdout由返回的文件描述符pipe(2)。我还没有找到任何关于此的信息。


请注意,您所谈论的是两个具有相同名称的截然不同的事物层。在pipe()与机器支持它(以及内核调用pipefs等)比低得多的水平|在你的shell提供的运营商。后者通常使用前者来实现,但不是必须如此。
格雷格·休吉尔

是的,我具体是指较低级别的操作,并假设|操作员只是pipe(2)像bash 一样调用进程。
Brandon Wamboldt 2014年

Answers:


19

到目前为止,您的分析通常是正确的。Shell可以将进程的标准输入设置为管道描述符的方式可能是(伪代码):

pipe(p) // create a new pipe with two handles p[0] and p[1]
fork() // spawn a child process
    close(p[0]) // close the write end of the pipe in the child
    dup2(p[1], 0) // duplicate the pipe descriptor on top of fd 0 (stdin)
    close(p[1]) // close the other pipe descriptor
    exec() // run a new process with the new descriptors in place

谢谢!只是好奇为什么dup2需要调用,而不能直接将管道描述符分配给stdin?
Brandon

3
在中创建文件描述符时,调用者不会选择它的数字值pipe()。该dup2()调用允许调用者将文件描述符复制到特定的数值(之所以需要,是因为0、1、2是stdin,stdout,stderr)。这与“直接分配给stdin”的内核等效。请注意,C运行时库全局变量stdin是a FILE *,与内核无关(尽管它已初始化为连接到描述符0)。
Greg Hewgill 2014年

好答案!我对细节有些迷惑。只是想知道为什么在运行exec()之前先关闭(p [1])?dup2返回后,p [1]不会指向fd 0吗?然后close(p [1])关闭文件描述符0。那么我们如何从子进程的stdin中读取?
user1559897 '18

@ user1559897:dup2通话不会更改p[1]。相反,它使两个手柄p[1],并0指向同一个内核对象(管道)。由于子进程不需要两个stdin句柄(并且也不知道到底是哪个编号的句柄p[1]),p[1]因此在之前将其关闭exec
格雷格·休吉尔

@GregHewgill Gotchu。谢谢!
user1559897 '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.