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

dan*_*ben 8 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
Run Code Online (Sandbox Code Playgroud)

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

Tam*_*más 8

socket.recv()调用recvC层中的基础POSIX兼容函数,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
Run Code Online (Sandbox Code Playgroud)

  • 请注意:根据PEP-3151,这在python 3.3中已弃用.而应该使用OSError内置. (2认同)