Unix Pipes

来自osdev
跳到导航 跳到搜索

Pipe管道,socketpairs套接字对和FIFO是允许两个进程通过字节流交换数据的技术。 但是,与文件不同,管道和其它技术不消耗磁盘空间,而是在内核中具有 (通常为循环队列) 缓冲区。 如果缓冲区变得过载 (例如,因为 “消费者” 太慢),则系统将 “生产者” 变为 “等待” 状态。

管道通常是单向流,具有 “生产者” 侧和 “消费者” 侧。 请注意,完全有可能拥有多个生产者和/或多个消费者 (尽管多个消费者倾向于使事情难以使用)。 论坛上有一个关于这个主题的讨论; Stream-oriented programming

用法

除了通常的shell脚本使用管道 (其中 grepless 等程序链接在一起,因此 grep 的输出变为 较少 的输入> grep kernel | less ),一些unix程序使用这种技术在预构建过程中传递数据,其中最著名的例子之一是 qmail。 例如,在运行GCC时,使用 -pipe 标志将使用管道而不是临时文件保存中间编译结果,从而加快编译速度。

终端仿真器也可以使用管道将电传打字机 (例如/dev/tty1) 与shell的标准输入和输出连接起来。 执行命令时,shell将继承的文件描述符与子进程的新文件描述符一起创建一个新管道,并将子进程的输入和输出与电传打字机设备连接起来。 例如,如果您在终端中发出 cat 命令,这就是cat的stdot中的文件内容到达/dev/tty1的方式,该文件由tty内核子系统读取以在控制台上呈现适当的字符。

实现

在Unix系统上有两个用于创建管道的命令: pipe() 和mkfifo()。 两者的行为方式相似-第一个创建 “未命名” 或 “匿名” 管道,第二个创建 “命名” 管道。 命名管道出现在文件系统中并存在,直到显式删除; 未命名管道不存在,并且仅在有至少一个文件描述符在其上打开时存在。

在Unix上创建 (未命名) 管道的标准方法是调用pipe(),它 “返回” 两个文件描述符。 一个用于写入管道,另一个用于从管道读取。

在内部,管道 (通常) 是一个循环缓冲区/队列,以及两个或多个用于锁定的信号量。 缓冲区大小 (在Linux上) 通常约为4K字节。

只要管道的任一端打开,就存在这个管道。两端都close() 后管道将其清除。 在典型的unix系统上,有几种方法可以将前面生成的两个文件描述符从一个进程传递到另一个进程; 最常见和 “正确” 的方式是通过fork() 调用将文件描述符从父级传递给子级。

如果其中一个文件描述符被关闭,而另一个试图被使用 (无论是用于读还是写),你会得到一个错误编号EPIPE (Broken pipe破裂的管道)。

当你写一个管道命令行,是shell调用 的pipe(),然后当它fork() 的进程做的工作,shell会做一些文件描述符重命名 (请参阅dup()),以在它最终exec() 的程序之前在正确的位置获得正确的描述符。 记住fork()得到的子进程默认继承父母拥有的文件描述符。

命名管道是相似的,除了它们位于文件系统中的某个位置,并且如果重复打开命名管道,则每次都会得到相同的管道。 创建后,命名管道将保留在文件系统中,直到像其他文件一样被删除。

另见