2010-06-10 8 views
8

Napisałem robota sieciowego, który chciałbym móc zatrzymać za pomocą klawiatury. Nie chcę, żeby program umarł, kiedy mu przerywam; najpierw musi przepłukać swoje dane na dysk. Nie chcę również złapać KeyboardInterruptedException, ponieważ trwałe dane mogą być niespójne.Catching/blocking SIGINT podczas wywołania systemowego

Moje obecne rozwiązanie to zdefiniowanie procedury obsługi sygnału, która przechwyci SIGINT i ustawi flagę; każda iteracja pętli głównej sprawdza tę flagę przed przetworzeniem następnego adresu URL.

jednak odkryłem, że jeśli system dzieje się wykonywanie socket.recv() kiedy wysłać przerwanie, mam to:

^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 

i proces wychodzi całkowicie. Dlaczego to się dzieje? Czy istnieje sposób, aby zapobiec zakłóceniom wpływającym na wywołanie systemowe?

Odpowiedz

7

socket.recv() wywołuje podstawowej POSIX zgodny recv funkcję warstwy C, która z kolei Zwraca kod błędu EINTR gdy proces otrzymuje SIGINT czekając danych przychodzących do recv(). Ten kod błędu może być użyty po stronie C (jeśli programowałeś w C), aby wykryć, że recv() nie został zwrócony, ponieważ więcej danych jest dostępnych na gnieździe, ale ponieważ proces ten otrzymał SIGINT. W każdym razie ten kod błędu jest w Pythonie przerobiony na wyjątek, a ponieważ nigdy nie zostanie przechwycony, kończy działanie aplikacji z widocznym prześwitem. Rozwiązaniem jest po prostu złapać socket.error, sprawdzić kod błędu i jeśli jest on równy errno.EINTR, zignorować wyjątek po cichu. Coś takiego:

import errno 

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

Świetne wyjaśnienie, dziękuję. – danben

+1

Używanie magicznej liczby 4 w miejsce 'EINTR' lub innego identyfikatora, który zapewnia Python, jest bardzo złą praktyką. Prawdopodobnie złamie niektóre łuki. –

+0

Jasne, masz rację. Po raz kolejny czytałem dokumenty z biblioteki Pythona i wygląda na to, że moduł 'errno' zapewnia te stałe, więc dostosuję przykład. –

3

Jeśli nie chcesz, aby Twoje połączenie gniazda zostać przerwana wyłączyć przerwania zachowanie po ustawieniu obsługi sygnału.

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

W funkcji obsługi sygnałów ustaw flagę, np. a threading.Event(), a następnie sprawdź tę flagę w głównej funkcji przetwarzania i z wdzięcznością zakończ robot gąsienicowy.

informacji Tło tutaj:

Powiązane problemy