Python 2中自定义信号处理器存在时读取sys.stdin的异常阻塞行为
分析Python脚本odd-read-blocking.py的信号与输入行为
先来看这个脚本的完整代码:
#!/usr/bin/python import signal import sys sig = None def handler(signum, frame): global sig sig = signum signal.signal(signal.SIGINT, handler) signal.signal(signal.SIGTERM, handler) x = sys.stdin.read(3) print 'signal', sig print 'read bytes', len(x) exit(0)
正常运行的场景
当我们用echo a | ./odd-read-blocking.py传入两个字节(a加换行符)时,因为echo输出后会直接关闭标准输入,sys.stdin.read(3)会读取到所有可用的2个字节后返回,脚本输出如下:
signal None
read bytes 2
这完全符合预期——没有收到任何信号,且读取了所有输入内容。
信号中断阻塞读取的场景
如果我们不用echo,而是直接运行脚本后手动输入a加回车(同样是两个字节),此时sys.stdin.read(3)会进入阻塞状态:因为还没凑够要求的3个字节,且标准输入没有被关闭。这时候如果我们发送SIGINT(比如按下Ctrl+C),会发生什么?
这里要划个重点:Python的默认行为中,被信号中断的read系统调用会抛出IOError,但因为我们自定义了信号处理函数,逻辑就变了——信号处理函数只会把sig变量设为对应的信号编号(比如SIGINT对应2),处理完成后,程序会回到原来的阻塞read调用,继续等待输入,直到输入流关闭或者凑够3个字节。
举个实际测试的例子:
- 运行
./odd-read-blocking.py - 输入
a加回车 - 按下Ctrl+C发送信号
- 按下Ctrl+D关闭标准输入
此时脚本会输出:
signal 2
read bytes 2
如果希望信号触发后立即终止read操作,可以修改信号处理函数,主动抛出异常或者直接退出程序,比如:
def handler(signum, frame): global sig sig = signum sys.exit(1) # 直接终止程序
这样收到信号后,脚本会立刻停止阻塞读取,输出捕获到的信号和已读取的字节数。
内容的提问来源于stack exchange,提问作者HerrB
相关产品推荐
相关产品推荐

