Перехват / блокировка SIGINT во время системного вызова - PullRequest
8 голосов
/ 10 июня 2010

Я написал веб-сканер, который я бы хотел остановить с помощью клавиатуры.Я не хочу, чтобы программа умерла, когда я ее прерываю;сначала необходимо сбросить данные на диск.Я также не хочу перехватывать 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

и процесс завершается полностью.Почему это происходит?Можно ли как-то предотвратить прерывание, влияющее на системный вызов?

Ответы [ 2 ]

8 голосов
/ 10 июня 2010

socket.recv() вызывает базовую POSIX-совместимую функцию recv на уровне C, которая, в свою очередь, вернет код ошибки 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
3 голосов
/ 18 июня 2015

Если вы не хотите, чтобы ваш вызов сокета прерывался, отключите поведение прерывания после установки обработчика сигнала.

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

В функции обработки сигнала установите некоторый флаг, например threading.Event (), а затем проверьте этот флаг в вашей основной функции обработки и корректно завершите работу вашего сканера.

Справочная информация здесь:

...