在系统调用期间捕获/阻止SIGINT

时间:2010-06-10 16:30:18

标签: python unix signals interrupt system-calls

我写过一个网络抓取工具,我希望能够通过键盘停下来。当我打断它时,我不希望程序死掉;它需要先将其数据刷新到磁盘。我也不想捕获KeyboardInterruptedException,因为持久数据可能处于不一致状态。

我目前的解决方案是定义一个捕获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

并且该过程完全退出。为什么会这样?有没有办法可以阻止中断影响系统调用?

2 个答案:

答案 0 :(得分:8)

socket.recv()调用C层中与POSIX兼容的基础recv函数,当进程收到EINTR时,该函数将返回错误代码SIGINT等待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

答案 1 :(得分:3)

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

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

在信号处理功能中设置一些标志,例如一个threading.Event()然后在主处理函数中检查该标志并正常终止你的爬虫。

背景信息: