我有两个简单的程序:A
和B
。 A
首先运行,然后B
获取“stdout”A
并将其用作“stdin”。假设我使用的是 GNU/Linux 操作系统,最简单的方法是:
./A | ./B
如果我必须描述这个命令,我会说它是一个从生产者 ( A
) 获取输入(即读取)并写入消费者 ( B
) 的命令。这是正确的描述吗?我错过了什么吗?
答案1
你的问题唯一突出的是错误的你说的是
A 首先运行,然后 B 获取 A 的标准输出
事实上,这两个程序几乎同时启动。如果在尝试读取时没有输入B
,它将阻塞,直到有输入要读取。同样,如果没有人读取 的输出A
,则其写入将被阻塞,直到读取其输出(其中一些将由管道缓冲)。
唯一同步参与管道的进程的是 I/O,即跨管道的读写。如果没有发生写入或读取,那么这两个进程将完全独立运行。如果一个进程忽略另一个进程的读取或写入,则被忽略的进程将阻塞并最终被信号杀死SIGPIPE
(如果是写入),或者当另一个进程终止时在其标准输入流上获得文件结束条件(如果是读取) 。
惯用的描述方式A | B
是它是一个包含两个程序的管道。第一个程序在标准输出上产生的输出可供第二个程序在标准输入上读取(“[的输出]通过A
管道传输到[的输入] B
”)。 shell 执行所需的管道操作以实现此目的。
如果你想使用“消费者”和“生产者”这两个词,我想也可以。
这些是用 C 编写的程序这一事实并不相关。事实上,这是 Linux、macOS、OpenBSD 或 AIX 并不相关。
答案2
文档中通常使用的术语是“管道”,它由一个或多个命令组成,参见 POSIX 定义 所以从技术上讲,这是两个命令,两个 shell 子进程(fork()+exec()
“ed 外部命令”或“子 shell”)
至于生产者-消费者部分,管道可以用该模式来描述,因为:
- 生产者和消费者共享固定大小的缓冲区,至少在 Linux 和 MacOS X 上,有管道缓冲区的固定大小
- 生产者和消费者是松耦合的,管道中的命令不知道彼此的存在(除非它们主动检查
/proc/<pid>/fd
目录)。 - 生产者写入
stdout
和消费者读取stdin
就好像它们是正在执行的单个命令,也称为它们可以没有彼此而存在。
我在这里看到的区别是,与其他语言中的生产者-消费者不同,shell 命令使用缓冲,一旦缓冲区填满,它们就会写入标准输出,但没有提到生产者-消费者必须遵循该规则 - 仅在队列填满或丢弃时等待数据(这是管道不做的其他事情)。