我遇到过各种文章和 SO 问题,但我仍然对我每天使用的东西感到困惑,但从未意识到它有多么令人困惑。我正在 Linux 中试验(命名)管道。
第一次 尝试很简单:弄清楚管道缓冲区是如何工作的:
#1
mkfifo /tmp/mypipe
#2
echo "Hello World" >/tmp/mypipe
ctrl+c
#3
cat /tmp/mypipe
Run Code Online (Sandbox Code Playgroud)
观察:当我echo
在cat
读取数据之前杀死时,没有任何内容写入管道(cat
继续运行但没有从管道中读取任何内容)。我假设当您输入producent >named_pipe
并退出时producent
,匹配管道缓冲区大小的部分数据将被写入named_pipe
并保留在此处直到被读取consument
(现在我知道这不是它的工作方式)。所以我接下来做的是:
第二次
尝试是连接consument
到管道的另一端:
#1
mkfifo /tmp/mypipe
#2
echo "Hello World" >/tmp/mypipe
#3
cat /tmp/mypipe
Run Code Online (Sandbox Code Playgroud)
观察:
cat
命令显示"Hello World"
消息并且两个进程都结束。这里有趣的发现是在#2 步骤期间ps -elf
不显示echo
命令。似乎echo
正在等待有人从管道中读取数据,这就是为什么在我的第一次尝试中没有向管道打印任何内容的原因。
第三次 尝试是管道命令将“永远”运行并不断写入管道,看看会发生什么:
#1
mkfifo /tmp/mypipe
#2
yes >/tmp/mypipe
#3
cat /tmp/mypipe
Run Code Online (Sandbox Code Playgroud)
观察:这按预期工作并cat
打印出yes
转发到管道的内容。但是我试图cat
用tail -f
. 当我这样tail
做时,直到yes
命令被终止才打印任何内容。
第四次 尝试是个大谜团:
# 1#
mkfifo /tmp/mypipe
# 2#
for i in $(seq 1 10000); do echo -n $i"|"> /tmp/mypipe; done
# 3#
for i in $(seq 1 10); do echo "${i}# Read:"; cat /tmp/mypipe && echo ""; done
Run Code Online (Sandbox Code Playgroud)
在此之后 3# 命令开始输入类似的内容:
1# Read:
1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31|32|33|34|35|36|37|38|39|40|41|42|43|44|45|46|47|48|49|50|51|52|53|54|55|56|57|58|59|60|61|62|63|64|65|66|67|68|69|70|71|72|73|74|75|76|77|78|79|80|81|82|83|84|85|86|87|88|89|90|91|92|93|94|95|96|97|98|99|100|101|102|103|104|105|106|107|108|
2# Read:
109|
3# Read:
110|
4# Read:
111|
5# Read:
112|
6# Read:
113|114|115|
7# Read:
116|
8# Read:
117|
9# Read:
118|119|120|121|122|123|124|125|126|127|128|129|130|131|132|133|134|135|136|137|138|139|140|141|142|143|144|145|146|147|148|149|150|151|152|153|154|155|156|157|158|159|160|161|162|163|164|165|166|167|168|169|170|171|172|173|174|175|176|177|178|179|180|181|182|183|184|185|186|187|188|189|190|191|192|193|194|195|196|197|198|199|200|201|202|203|204|205|206|207|208|209|210|211|212|213|214|215|216|217|218|219|220|221|222|223|224|225|226|227|228|229|230|231|232|233|234|235|236|237|238|239|240|241|242|243|244|245|246|247|248|249|250|251|252|253|254|255|256|257|258|259|260|261|262|263|264|265|266|267|268|269|270|271|272|273|274|275|276|277|278|279|280|281|282|283|284|285|286|287|288|289|290|291|292|293|294|295|
10# Read:
296|297|298|299|300|301|302|303|304|305|306|307|308|309|310|311|312|313|314|315|316|317|318|319|320|321|322|323|324|325|326|327|328|329|
Run Code Online (Sandbox Code Playgroud)
问题:
第一次和第二次尝试:
|
在这种特殊情况下,命名管道是否等同于经典管道,例如从 bash 知道的?Linux 如何知道消费者何时连接到管道以及何时可以进行通信?我试过了,lsof named_pipe
但它什么也没给我,这些信息存储在哪里?我也尝试过,结果是cat
无法从管道中读取。
#1
mkfifo /tmp/mypipe
#2
echo 1 >/tmp/mypipe
#3
rm /tmp/mypipe
#4
mkfifo /tmp/mypipe
#5
cat /tmp/mypipe
Run Code Online (Sandbox Code Playgroud)是打字:producent >/tmp/mypipe
相当于打字command |
我的意思是有人想将一个命令通过管道传输到另一个命令但忘记在管道之后键入另一个命令的ps
情况(在这种情况下也没有先显示command
)?
第三次尝试:
cat
和之间有什么区别tail -f
?第四次尝试:
这里发生了什么?为什么读取的数据块不是确切的大小?我期望输出为:
1# 阅读:1| 2# 阅读:2| 3# 阅读:3|
PS:我也尝试过不同的启动命令顺序(先读后写),但结果是一样的。
PPS:我希望这很清楚,但是:生产者 = 写入管道的进程。消费者 = 从管道读取的进程。
这是否可以向主要具有一点 C 脚本知识的人解释?非常感谢。
编辑回复:Joe Sewell
我知道两者并行运行,或者换句话说,以下两个不一样:
find | less
Run Code Online (Sandbox Code Playgroud)
对比
find > /tmp/file && less /tmp/file
Run Code Online (Sandbox Code Playgroud)
我的进一步观察发现,当我运行以下时,硬盘似乎不工作,直到less
命令有足够的数据显示
find | less
Run Code Online (Sandbox Code Playgroud)
当我点击shifg+g
(转到 中的文件末尾less
)时,HDD 立即开始工作并且数据开始输出。这是否意味着当less
命令有足够的数据可以显示时,它会以某种方式告诉find
不产生更多数据?这就是你所说的同步?写入管道的数据量也对应于它的缓冲区大小?我也注意到,find
改变它的状态(ps aux
-从统计列)S+ to D+
我打后,shift+g
在less
S interruptible sleep (waiting for an event to complete)
D uninterruptible sleep (usually IO)
+ is in the foreground process group.
??[wakatana@~] [63 files, 178Mb]
???> ps aux | egrep -w 'less|find'
wakatana 6071 0.0 0.0 12736 1088 pts/5 S+ 23:15 0:00 find
wakatana 6072 0.0 0.0 7940 928 pts/5 S+ 23:15 0:00 less
wakatana 6183 0.0 0.0 7832 892 pts/6 S+ 23:20 0:00 egrep --color=auto -w less|find
??[wakatana@~] [63 files, 178Mb]
???> ps aux | egrep -w 'less|find'
wakatana 6071 0.0 0.0 12808 1304 pts/5 D+ 23:15 0:00 find
wakatana 6072 0.0 0.0 9556 2508 pts/5 S+ 23:15 0:00 less
wakatana 6193 0.0 0.0 7832 892 pts/6 S+ 23:21 0:00 egrep --color=auto -w less|find
Run Code Online (Sandbox Code Playgroud)
谁发送这个信号,消费到生产?如果是,那么消费者如何知道他已连接到已经生产的管道(例如我使用 rm 管道的示例)?
确定 清除
确定 清除
我认为新的线条不是让我感到困惑的情况。根据我之前的观察(并且您确认:“是的,两端都在等待对方。”)。我期待这个:
I. 第一次循环中的第一次迭代将写入管道,因为没有人正在阅读它会在这里等待。
二、当发出第二个循环时,将读取在第一次迭代中由第一个循环写入的数据,此处不再写入任何内容,因此无法读取更多内容。
三、第二个循环将等待第一个循环写入下一个数据或(因为顺序无关)第一个循环将等待直到第二个循环读取写入的数据,依此类推。
因此,我期望一次写入对应一次读取。我也在验证循环是否没有运行,所以我修改了一个原始命令,看看即使消耗者不会读取某些内容,是否也会将某些内容打印到 STDOUT,但没有打印任何内容。
for i in $(seq 1 10000); do
if [ $(( $i % 5 )) -eq 0 ]; then
echo $i;
else
echo -n $i"|"> /tmp/mypipe;
fi;
done
Run Code Online (Sandbox Code Playgroud)
“由于写入过程没有发送任何换行符,读者只需阅读,直到它被告知它“足够了”。”
“在第一种情况下,可能是因为 fifo 的缓冲区已满,”
“因此被读者通读了。”
“虽然有一些方法可以使通信异步......”
要按数字回答您的问题列表:
命名管道,又名 fifos,本质上等同于外壳生成的未命名管道。最大的区别是两端之间的同步在 shell 版本中是直观的,而命名管道,因为你似乎在使用它们,需要一些关于 shell 为你做什么的知识。
是的,两端都在等待对方。fifos 的目的,就像 shell 管道一样,是将一个进程的输出传递到另一个进程的输入。它们不是临时文件。我怀疑这就是你感到困惑的地方。在 shell 命令的情况下cat somefile.txt | less
,两个命令作为分叉进程同时运行,管道用于同步两者。如果没记错的话,这可以在 C 中修改,但使用 shell 命令则不那么容易。
当管道的另一端获得连接时,进程可以接收信号,但通常整个意图,如上所述,是保持两个进程同步。写入器发送一些东西,它知道当写入操作完成时它可以继续。
bash
并且tcsh
不会让您“遗忘”。该命令甚至没有运行。
tail -f
stdin
在它可以显示任何内容之前,必须读取整个流,直到它获得 EOF,在本例中为 on 。在你的实验中,终点从未出现。cat
,另一方面,可以立即开始处理其输入。
由于写入过程不会发送任何换行符,因此读者只需阅读,直到它被告知“足够了”。在第一种情况下,可能是因为 fifo 的缓冲区已满,因此被刷新到读取器。随后的输出可能类似,并且可能会根据系统时序而有所不同。
让我在这里解决另一个困惑。shell在运行命令之前处理重定向。这意味着您不会cat
在进程列表中看到,因为在运行或涉及任何bash
编写器之前一直在等待 fifo 的另一端连接cat
。同样,在连接写入器之前,它不会执行读取命令。
我认为您在这里最大的误解是命名管道不是临时文件。也不是未命名的管道。虽然有一些方法可以使通信异步,但看起来最好在 下创建实际的临时文件/tmp
,除非您确实希望两个进程同时运行。
归档时间: |
|
查看次数: |
4662 次 |
最近记录: |