2010-06-10 57 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(),然後檢查你的主要處理函數中的這個標誌並優雅地終止你的爬蟲。

背景信息在這裏: