Python select() 的行为很奇怪
我在理解select.select的行为时遇到了一些问题。请看下面这个Python程序:
def str_to_hex(s):
def dig(n):
if n > 9:
return chr(65-10+n)
else:
return chr(48+n)
r = ''
while len(s) > 0:
c = s[0]
s = s[1:]
a = ord(c) / 16
b = ord(c) % 16
r = r + dig(a) + dig(b)
return r
while True:
ans,_,_ = select.select([sys.stdin],[],[])
print ans
s = ans[0].read(1)
if len(s) == 0: break
print str_to_hex(s)
我把这个程序保存到了一个名为“test.py”的文件里。如果我这样运行它:
echo 'hello' | ./test.py
那么我就能得到预期的效果:select不会阻塞,所有的数据都会被打印出来;程序随后结束。
但是如果我在交互式环境中运行这个程序,就会出现非常不理想的情况。请看下面这个控制台会话:
$ ./test.py
hello
[<open file '<stdin>', mode 'r' at 0xb742f020>]
68
程序就卡在那儿;select.select又开始阻塞了。直到我输入更多内容或者关闭输入流,接下来的字符(以及所有后面的字符)才会被打印出来,尽管其实已经有字符在等待了!有没有人能给我解释一下这种行为?我在一个流隧道程序中也遇到了类似的问题,这让整个事情变得很糟糕。
谢谢你的阅读!
2 个回答
它在等你发出结束信号(如果你在交互模式下,可以按 Ctrl+D 来实现)。你可以用 sys.stdin.isatty()
来检查这个脚本是否在交互模式下运行,然后根据情况来处理,比如可以使用 raw_input
。我也觉得你根本不需要用 select.select
,为什么不直接用 sys.stdin.read
呢?
if sys.stdin.isatty():
while True:
for s in raw_input():
print str_to_hex(s)
else:
while True:
for s in sys.stdin.read(1):
print str_to_hex(s)
这样做既适合交互使用,也适合流处理。
sys.stdin
的 read
方法比 select
更高级一些。当你执行 ans[0].read(1)
时,实际上 Python 会从操作系统读取更多的字节,并在内部进行缓存。select
并不知道这些额外的缓存;它只知道所有数据都已经被读取,所以会一直等待,直到遇到文件结束符(EOF)或者有新的输入到来。你可以通过运行类似 strace -e read,select python yourprogram.py
的命令来观察这种行为。
一个解决方法是把 ans[0].read(1)
替换成 os.read(ans[0].fileno(), 1)
。os.read
是一个更底层的接口,它和操作系统之间没有任何缓存,所以它和 select
更匹配。
另外,使用 -u
这个命令行选项来运行 python
似乎也能禁用额外的缓存。