2010-06-10 5 views
8

키보드를 통해 멈추고 싶은 웹 크롤러를 작성했습니다. 프로그램을 중단 할 때 프로그램이 죽는 것을 원하지 않습니다. 먼저 디스크에 데이터를 플러시해야합니다. 또한 영구 데이터가 일관성이없는 상태 일 수 있으므로 KeyboardInterruptedException을 포착하고 싶지 않습니다.시스템 콜 중에 SIGINT 잡기/차단하기

내 현재 해결 방법은 SIGINT을 포착하고 플래그를 설정하는 신호 처리기를 정의하는 것입니다. 메인 루프의 각 반복은 다음 url을 처리하기 전에이 플래그를 검사합니다.

^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 

프로세스가 완전히 종료 :

그러나, 나는 시스템이 내가 인터럽트를 보낼 때 socket.recv()을 실행하는 일이 생기면, 나는이를 얻을 것으로 나타났습니다. 왜 이런 일이 생길까요? 인터럽트가 시스템 호출에 영향을 미치지 않도록하는 방법이 있습니까?

답변

7

socket.recv()recv()에서 들어오는 데이터를 대기하는 동안 처리가 SIGINT를 받으면 다시, 에러 코드를 반환 EINTR의 C 층의 하부 POSIX 호환 recv 함수를 호출한다. 이 오류 코드는 C에서 프로그래밍 할 경우 C에서 사용할 수 있습니다. 이 반환 된 것은 소켓에서 사용할 수있는 데이터가 더 많기 때문이 아니라 해당 프로세스가 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

+1

'EINTR'대신 파이썬이 제공하는 식별자가 무엇이든간에 매직 넘버 4를 사용하는 것은 매우 나쁜 습관입니다. 그것은 어떤 아치를 깨기 쉽습니다. –

+0

물론, 당신 말이 맞아요. 파이썬 라이브러리 문서를 다시 읽었고'errno' 모듈이 이러한 상수를 제공하는 것으로 보이므로 예제를 조정할 것입니다. –

3

소켓 호출을 중단하지 않으려면 신호 처리기를 설정 한 후 인터럽트 동작을 비활성화하십시오.

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

신호 처리 기능에서 일부 플래그를 설정합니다. threading.Event()를 호출 한 다음 주 처리 함수에서 해당 플래그를 확인하고 정상적으로 크롤러를 종료하십시오. 여기

배경 정보 : SA_RESTART 플래그에 대한