在系统调用中捕获/阻塞SIGINT
我写了一个网络爬虫,想通过键盘来停止它。我不希望程序在我中断时直接崩溃,而是需要先把数据保存到硬盘上。我也不想捕捉 KeyboardInterruptedException
,因为这样可能会导致数据不一致。
我现在的解决办法是定义一个信号处理器,专门用来捕捉 SIGINT
信号,并设置一个标志;在主循环的每次迭代中,都会检查这个标志,看看是否需要处理下一个网址。
不过,我发现如果系统在执行 socket.recv()
的时候我发送了中断信号,就会出现这样的情况:
^C
Interrupted; stopping... // indicates my interrupt handler ran
Traceback (most recent call last):
File "crawler_test.py", line 154, in <module>
main()
...
File "/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/socket.py", line 397, in readline
data = recv(1)
socket.error: [Errno 4] Interrupted system call
然后进程就完全退出了。为什么会这样呢?有没有办法防止中断影响到系统调用?
2 个回答
9
socket.recv()
这个函数其实是在底层调用了一个叫 recv
的C语言函数。这个函数会在等待接收数据的时候,如果进程收到了一个叫 SIGINT
的信号,就会返回一个错误代码 EINTR
。这个错误代码在C语言中可以用来判断 recv()
返回的原因,不是因为有新数据,而是因为收到了 SIGINT
的信号。不过在Python中,这个错误代码会被转化成一个异常,而这个异常如果没有被处理,就会导致你的程序崩溃,并显示出错误追踪信息。解决这个问题的方法很简单,就是捕获 socket.error
,然后检查错误代码,如果它等于 errno.EINTR
,就悄悄地忽略这个异常。大概可以这样写:
import errno
try:
# do something
result = conn.recv(bufsize)
except socket.error as (code, msg):
if code != errno.EINTR:
raise