2010-06-10 6 views
8

Ich habe einen Web-Crawler geschrieben, den ich gerne über die Tastatur anhalten könnte. Ich möchte nicht, dass das Programm stirbt, wenn ich es unterbreche; Es muss zuerst seine Daten auf der Festplatte löschen. Ich möchte auch nicht KeyboardInterruptedException fangen, weil die persistenten Daten in einem inkonsistenten Zustand sein könnten.SIGINT während des Systemaufrufs abfangen/blockieren

Meine aktuelle Lösung besteht darin, einen Signalhandler zu definieren, der SIGINT abfängt und ein Flag setzt; Jede Iteration der Hauptschleife prüft dieses Flag vor der Verarbeitung der nächsten URL.

Allerdings habe ich festgestellt, dass, wenn das System socket.recv() Ausführung werden passiert, wenn ich den Interrupt senden, erhalte ich dieses:

^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 

und der Prozess verlässt vollständig. Warum passiert das? Kann ich verhindern, dass der Interrupt den Systemaufruf beeinflusst?

Antwort

7

socket.recv() ruft die darunter liegende POSIX-konforme recv Funktion in der C-Schicht, die wiederum einen Fehlercode zurück, wenn EINTR der Prozess ein SIGINT empfängt, während für die eingehenden Daten in recv() wartet. Dieser Fehlercode kann auf der C-Seite verwendet werden (wenn Sie in C programmierten), um zu erkennen, dass recv() nicht zurückgegeben wurde, da auf dem Socket mehr Daten verfügbar sind, sondern weil der Prozess eine SIGINT empfangen hat. Wie auch immer, dieser Fehlercode wird von Python in eine Ausnahme umgewandelt, und da er nie abgefangen wird, beendet er Ihre Anwendung mit dem Traceback, den Sie sehen. Die Lösung ist einfach zu fangen socket.error, überprüfen Sie den Fehlercode und wenn es gleich errno.EINTR ist, ignorieren Sie die Ausnahme im Hintergrund. Etwas wie folgt aus:

import errno 

try: 
    # do something 
    result = conn.recv(bufsize) 
except socket.error as (code, msg): 
    if code != errno.EINTR: 
     raise 
+0

Große Erklärung, danke. – danben

+1

Die Verwendung der magischen Zahl 4 anstelle von 'EINTR' oder welcher Bezeichner Python dafür bereitstellt, ist eine sehr schlechte Übung. Es wird wahrscheinlich einige Bögen brechen. –

+0

Sicher hast du recht. Ich lese die Python-Bibliotheksdokumente noch einmal und es scheint, dass das 'errno'-Modul diese Konstanten bereitstellt, so dass ich das Beispiel anpassen werde. –

3

Wenn Sie nicht möchten, dass Ihre Socket-Aufruf unterbrochen werden deaktivieren Sie das Interrupt-Verhalten, nachdem Sie den Signal-Handler gesetzt.

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

In der Signalverarbeitungsfunktion wurde ein Flag gesetzt, z. a threading.Event() und dann dieses Flag in Ihrer Hauptverarbeitungsfunktion überprüfen und Ihren Crawler ordnungsgemäß beenden.

Hintergrundinfo hier:

Verwandte Themen