2010-06-10 54 views
8

我写了一个网络爬虫,希望能够通过键盘停下来。我不希望程序在我中断时死掉;它需要先将数据刷新到磁盘。我也不想捕获KeyboardInterruptedException,因为持久数据可能处于不一致的状态。在系统调用期间捕获/阻止SIGINT

我目前的解决方案是定义一个信号处理程序,捕获SIGINT并设置一个标志;主循环的每次迭代在处理下一个url之前检查该标志。

然而,我发现,如果系统恰好是执行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 

和过程完全退出。为什么会发生?有没有办法阻止中断影响系统调用?

回答

7

socket.recv()调用在C层,这反过来,将当进程接收到SIGINT而在recv()等待输入数据返回错误代码EINTR底层符合POSIX的recv功能。此错误代码可用于C端(如果您使用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 
+0

很好的解释,谢谢。 – danben 2010-06-10 19:11:12

+1

使用幻数4代替'EINTR'或Python提供的任何标识符是非常糟糕的做法。它很可能在某些拱门上打破。 – 2013-03-25 15:39:28

+0

当然,你是对的。我再次阅读Python库文档,似乎'errno'模块提供了这些常量,所以我将调整示例。 – 2013-03-25 19:07:53

3

如果您不希望套接字调用被中断,请在设置信号处理程序后禁用中断行为。

signal.signal(<your signal here>, <your signal handler function here>) 
signal.siginterrupt(<your signal here>, False) 

在信号处理函数中设置了一些标志,例如,一个threading.Event(),然后检查你的主要处理函数中的这个标志并优雅地终止你的爬虫。

背景信息在这里: