以下是示例程序:
from = File.open('test.bin', 'rb')
to = IO.popen('cat', 'w+b')
i = 0
while buff = from.read(4096)
to.write(buff) # blocks here
i += 1
print "#{i}, #{buff.size} bytes read \r"
end
它读取二进制文件(您可以在Linux上使用fallocate -l 1MB test.bin
创建它)并管道到cat
命令。
然而,to.write(buff)
来电挂起。以下是它在控制台中的外观:
[1] pry(main)> from = File.open('test.bin', 'rb')
#<File:test.bin>
-rw-rw-r-- 1 admin admin 1000000 Mar 30 13:38 test.bin
[2] pry(main)> to = IO.popen('cat', 'w+b')
#<IO:fd 14>
[3] pry(main)> i = 0
0
[4] pry(main)>
[5] pry(main)> while buff = from.read(4096)
[5] pry(main)* to.write(buff)
[5] pry(main)* i += 1
[5] pry(main)* print "#{i}, #{buff.size} bytes read \r"
[5] pry(main)* end
33, 4096 bytes read
因此它只写入135168个字节。但为什么呢?
另外,如果我使用不同的命令(我需要它用于带有一些参数的gpg),字节数是不同的(大约60 MB),但结果是相同的,它在完全相同的点处阻塞(这一点是不同的对于cat
和gpg
,无论您运行该程序多少次,每个程序都保持不变。)
环境:Ubuntu Linux,ruby 2.3.4p301
答案 0 :(得分:2)
它阻塞的原因是因为IO.popen
打开一个带有写和读文件句柄以及相关读写缓冲区的管道。
由于您根本不从io
对象的读取句柄读取,因此读取缓冲区最终会变满(因为cat
不断将其输入复制到其输出),并且读取缓冲区已满,OS阻塞管道的写入侧,直到再次在读取缓冲区中有空间。
您已经有效地创建了死锁情况。
解决方案是继续从读取端读取,以防止读取缓冲区阻塞,或者不使命令输出任何内容。
从读句柄读取可能很棘手,因为它本身就是一个阻塞调用。您需要设置非阻塞读取,或者您可以使用单独的线程来进行读取。
然而,最简单的解决方案是阻止cat
输出任何内容,因此您永远不会创建死锁情况:
to = IO.popen('cat > /dev/null', 'w+b')
这可能是也可能不是你所追求的,但它应该给你一些关于如何继续的想法。