Перейти к содержимому

18.8. signal – Установка обработчиков для асинхронных событий


Этот модуль предоставляет механизмы для использования обработчиков сигналов в Python.

18.8.1. Общие правила

Функция signal.signal() позволяет определять пользовательские обработчики, выполняемые при получении сигнала. Установлено небольшое количество обработчиков по умолчанию: SIGPIPE игнорируется (так что ошибки записи в каналы и сокеты могут сообщаться как обычные исключения Python), а SIGINT преобразуется в исключение KeyboardInterrupt.

Обработчик для конкретного сигнала после установки остается активным до тех пор, пока не будет явно сброшен (Python эмулирует интерфейс в стиле BSD независимо от базовой реализации), за исключением обработчика для SIGCHLD, который следует базовой реализации.

18.8.1.1. Выполнение обработчиков сигналов Python

Обработчик сигналов Python не выполняется внутри низкоуровневого (C) обработчика сигналов. Вместо этого низкоуровневый обработчик устанавливает флаг, который сообщает виртуальной машине, что нужно выполнить соответствующий обработчик сигналов Python позже (например, на следующей инструкции байт-кода). Это приводит к следующим последствиям:

  • Мало смысла в перехвате синхронных ошибок, таких как SIGFPE или SIGSEGV, вызванных некорректной операцией в C-коде. Python вернется из обработчика сигнала в C-код, который, скорее всего, снова вызовет тот же сигнал, что приведет к видимому зависанию Python. Начиная с Python 3.3, можно использовать модуль faulthandler для сообщения о синхронных ошибках.

  • Длительное вычисление, реализованное чисто на C (например, сопоставление регулярного выражения с большим объемом текста), может выполняться без прерывания в течение произвольного времени, независимо от полученных сигналов. Обработчики сигналов Python будут вызваны после завершения вычисления.

18.8.1.2. Сигналы и потоки

Обработчики сигналов Python всегда выполняются в главном потоке Python, даже если сигнал был получен в другом потоке. Это означает, что сигналы нельзя использовать как средство межпотокового взаимодействия. Вместо этого можно использовать примитивы синхронизации из модуля threading.

Кроме того, только главному потоку разрешено устанавливать новый обработчик сигнала.

18.8.2. Содержимое модуля

Изменено в версии 3.5: константы signal (SIG*), handler (SIG_DFL, SIG_IGN) и sigmask (SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK), перечисленные ниже, были преобразованы в enums. Функции getsignal(), pthread_sigmask(), sigpending() и sigwait() возвращают человекочитаемые enums.

В модуле signal определены следующие переменные:

signal.SIG_DFL

Это один из двух стандартных вариантов обработки сигналов; он просто выполняет функцию по умолчанию для данного сигнала. Например, в большинстве систем действие по умолчанию для SIGQUIT – сбросить дамп ядра и завершиться, а для SIGCHLD – просто игнорировать его.

signal.SIG_IGN

Это ещё один стандартный обработчик сигналов, который просто игнорирует данный сигнал.

SIG*

Все номера сигналов определены символически. Например, сигнал разъединения (hangup) определен как signal.SIGHUP; имена переменных идентичны именам, используемым в программах на C, как в <signal.h>. Страница руководства Unix для ‘signal()’ перечисляет существующие сигналы (в некоторых системах это signal(2), в других список находится в signal(7)). Обратите внимание, что не все системы определяют одинаковый набор имен сигналов; модуль определяет только те имена, которые определены системой.

signal.CTRL_C_EVENT

Сигнал, соответствующий событию нажатия клавиш Ctrl+C. Этот сигнал можно использовать только с os.kill().

Доступность: Windows.

Новое в версии 3.2.

signal.CTRL_BREAK_EVENT

Сигнал, соответствующий событию нажатия клавиш Ctrl+Break. Этот сигнал можно использовать только с os.kill().

Доступность: Windows.

Новое в версии 3.2.

signal.NSIG

На единицу больше, чем максимальный номер сигнала.

signal.ITIMER_REAL

Уменьшает таймер интервалов в реальном времени и отправляет SIGALRM по истечении.

signal.ITIMER_VIRTUAL

Уменьшает таймер интервалов только при выполнении процесса и отправляет SIGVTALRM по истечении.

signal.ITIMER_PROF

Уменьшает таймер интервалов как при выполнении процесса, так и при выполнении системы от имени процесса. В паре с ITIMER_VIRTUAL этот таймер обычно используется для профилирования времени, затрачиваемого приложением в пользовательском и системном пространстве. SIGPROF отправляется по истечении.

signal.SIG_BLOCK

Возможное значение параметра how функции pthread_sigmask(), указывающее, что сигналы должны быть заблокированы.

Новое в версии 3.3.

signal.SIG_UNBLOCK

Возможное значение параметра how функции pthread_sigmask(), указывающее, что сигналы должны быть разблокированы.

Новое в версии 3.3.

signal.SIG_SETMASK

Возможное значение параметра how функции pthread_sigmask(), указывающее, что маска сигналов должна быть заменена.

Новое в версии 3.3.

Модуль signal определяет одно исключение:

exceptionsignal.ItimerError

Возбуждается для сообщения об ошибке в реализации setitimer() или getitimer(). Ожидайте этой ошибки, если в setitimer() передан недопустимый интервальный таймер или отрицательное время. Эта ошибка является подтипом OSError.

Новое в версии 3.3: Раньше эта ошибка была подтипом IOError, а теперь является псевдонимом OSError.

Модуль signal определяет следующие функции:

signal.alarm(time)

Если time не равно нулю, эта функция запрашивает отправку сигнала SIGALRM процессу через time секунд. Любой ранее запланированный сигнал будильника отменяется (в любой момент времени может быть запланирован только один сигнал). Возвращаемое значение – количество секунд до момента, когда должен был быть доставлен предыдущий установленный сигнал. Если time равно нулю, сигнал не планируется, а любой запланированный сигнал отменяется. Если возвращаемое значение равно нулю, сигнал в данный момент не запланирован. (См. страницу руководства Unix alarm(2).) Доступность: Unix.

signal.getsignal(signalnum)

Возвращает текущий обработчик сигнала для сигнала signalnum. Возвращаемое значение может быть вызываемым объектом Python или одним из специальных значений signal.SIG_IGN, signal.SIG_DFL или None. Здесь signal.SIG_IGN означает, что сигнал ранее игнорировался, signal.SIG_DFL – что ранее использовался способ обработки сигнала по умолчанию, а None – что предыдущий обработчик сигнала не был установлен из Python.

signal.pause()

Приостанавливает процесс до получения сигнала; затем будет вызван соответствующий обработчик. Ничего не возвращает. Недоступно в Windows. (См. страницу руководства Unix signal(2).)

См. также sigwait(), sigwaitinfo(), sigtimedwait() и sigpending().

signal.pthread_kill(thread_id, signalnum)

Отправляет сигнал signalnum потоку thread_id, другому потоку в том же процессе, что и вызывающий. Целевой поток может выполнять любой код (Python или нет). Однако если целевой поток выполняет интерпретатор Python, то обработчики сигналов Python будут выполнены главным потоком. Следовательно, единственный смысл отправки сигнала конкретному потоку Python – вынудить выполняющийся системный вызов завершиться с ошибкой InterruptedError.

Используйте threading.get_ident() или атрибут ident объектов threading.Thread, чтобы получить подходящее значение для thread_id.

Если signalnum равен 0, то сигнал не отправляется, но проверка ошибок всё равно выполняется; это можно использовать для проверки, работает ли целевой поток.

Доступность: Unix (см. страницу руководства pthread_kill(3) для дополнительной информации).

См. также os.kill().

Новое в версии 3.3.

signal.pthread_sigmask(how, mask)

Получает и/или изменяет маску сигналов вызывающего потока. Маска сигналов – это набор сигналов, доставка которых в настоящее время заблокирована для вызывающего. Возвращает старую маску сигналов в виде набора сигналов.

Поведение вызова зависит от значения how, как описано ниже.

  • SIG_BLOCK: Набор заблокированных сигналов является объединением текущего набора и аргумента mask.

  • SIG_UNBLOCK: Сигналы из mask удаляются из текущего набора заблокированных сигналов. Допускается попытка разблокировать сигнал, который не заблокирован.

  • SIG_SETMASK: Набор заблокированных сигналов устанавливается равным аргументу mask.

mask – это набор номеров сигналов (например, {signal.SIGINT, signal.SIGTERM}). Используйте range(1, signal.NSIG) для полной маски, включающей все сигналы.

Например, signal.pthread_sigmask(signal.SIG_BLOCK, []) читает маску сигналов вызывающего потока.

Доступность: Unix. См. страницы руководства sigprocmask(3) и pthread_sigmask(3) для дополнительной информации.

См. также pause(), sigpending() и sigwait().

Новое в версии 3.3.

signal.setitimer(which, seconds[, interval])

Устанавливает заданный интервальный таймер (один из signal.ITIMER_REAL, signal.ITIMER_VIRTUAL или signal.ITIMER_PROF), указанный параметром which, чтобы он срабатывал через seconds (допускается float, в отличие от alarm()) и затем каждые interval секунд. Интервальный таймер, указанный параметром which, можно очистить, установив seconds в ноль.

Когда интервальный таймер срабатывает, процессу отправляется сигнал. Отправляемый сигнал зависит от используемого таймера; signal.ITIMER_REAL доставит SIGALRM, signal.ITIMER_VIRTUAL отправляет SIGVTALRM, а signal.ITIMER_PROF доставит SIGPROF.

Старые значения возвращаются в виде кортежа: (delay, interval).

Попытка передать недопустимый интервальный таймер вызовет исключение ItimerError. Доступность: Unix.

signal.getitimer(which)

Возвращает текущее значение заданного интервального таймера, указанного параметром which. Доступность: Unix.

signal.set_wakeup_fd(fd)

Устанавливает файловый дескриптор пробуждения в fd. Когда получен сигнал, номер сигнала записывается в fd как один байт. Это может использоваться библиотекой для пробуждения вызова poll или select, позволяя полностью обработать сигнал.

Возвращается старый fd пробуждения (или -1, если пробуждение через файловый дескриптор не было включено). Если fd равен -1, пробуждение через файловый дескриптор отключено. Если не -1, fd должен быть неблокирующим. Библиотеке необходимо удалить все байты из fd перед повторным вызовом poll или select.

Используйте, например, struct.unpack('%uB' % len(data), data) для декодирования списка номеров сигналов.

Если потоки включены, эту функцию можно вызывать только из главного потока; попытка вызвать её из других потоков приведет к возникновению исключения ValueError.

Изменено в версии 3.5: В Windows функция теперь также поддерживает сокетные дескрипторы.

signal.siginterrupt(signalnum, flag)

Изменяет поведение перезапуска системных вызовов: если flag равно False, системные вызовы будут перезапускаться при прерывании сигналом signalnum, в противном случае системные вызовы будут прерваны. Ничего не возвращает. Доступность: Unix (см. страницу руководства siginterrupt(3) для дополнительной информации).

Обратите внимание, что установка обработчика сигнала с помощью signal() сбросит поведение перезапуска на прерываемое, неявно вызывая siginterrupt() со значением flag, равным истине, для данного сигнала.

signal.signal(signalnum, handler)

Устанавливает обработчик для сигнала signalnum в функцию handler. handler может быть вызываемым объектом Python, принимающим два аргумента (см. ниже), или одним из специальных значений signal.SIG_IGN или signal.SIG_DFL. Возвращается предыдущий обработчик сигнала (см. описание getsignal() выше). (См. страницу руководства Unix signal(2).)

Если потоки включены, эту функцию можно вызывать только из главного потока; попытка вызвать её из других потоков приведет к возникновению исключения ValueError.

handler вызывается с двумя аргументами: номером сигнала и текущим стековым фреймом (None или объектом фрейма; описание объектов фрейма смотрите в описании в иерархии типов или в описаниях атрибутов в модуле inspect).

В Windows signal() можно вызывать только с SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM или SIGBREAK. В любом другом случае будет возбуждено ValueError. Обратите внимание, что не все системы определяют одинаковый набор имён сигналов; будет возбуждено AttributeError, если имя сигнала не определено как константа уровня модуля SIG*.

signal.sigpending()

Проверяет набор сигналов, ожидающих доставки вызывающему потоку (т.е. сигналы, которые были возбуждены, пока были заблокированы). Возвращает набор ожидающих сигналов.

Доступность: Unix (см. страницу руководства sigpending(2) для дополнительной информации).

См. также pause(), pthread_sigmask() и sigwait().

Новое в версии 3.3.

signal.sigwait(sigset)

Приостанавливает выполнение вызывающего потока до доставки одного из сигналов, указанных в наборе сигналов sigset. Функция принимает сигнал (удаляет его из списка ожидающих сигналов) и возвращает номер сигнала.

Доступность: Unix (см. страницу руководства sigwait(3) для дополнительной информации).

Смотрите также pause(), pthread_sigmask(), sigpending(), sigwaitinfo() и sigtimedwait().

Новое в версии 3.3.

signal.sigwaitinfo(sigset)

Приостанавливает выполнение вызывающего потока до доставки одного из сигналов, указанных в наборе сигналов sigset. Функция принимает сигнал и удаляет его из списка ожидающих сигналов. Если один из сигналов в sigset уже ожидает доставки для вызывающего потока, функция немедленно возвращается с информацией об этом сигнале. Обработчик сигнала не вызывается для доставленного сигнала. Функция возбуждает InterruptedError, если она прервана сигналом, который не входит в sigset.

Возвращаемое значение – объект, представляющий данные, содержащиеся в структуре siginfo_t, а именно: si_signo, si_code, si_errno, si_pid, si_uid, si_status, si_band.

Доступность: Unix (см. страницу руководства sigwaitinfo(2) для дополнительной информации).

См. также pause(), sigwait() и sigtimedwait().

Новое в версии 3.3.

Изменено в версии 3.5: Теперь функция повторяется, если она прервана сигналом, не входящим в sigset, и обработчик сигнала не возбуждает исключение (см. PEP 475 для обоснования).

signal.sigtimedwait(sigset, timeout)

Как и sigwaitinfo(), но принимает дополнительный аргумент timeout, задающий тайм-аут. Если timeout задан как 0, выполняется опрос. Возвращает None при наступлении тайм-аута.

Доступность: Unix (см. man-страницу sigtimedwait(2) для дополнительной информации).

См. также pause(), sigwait() и sigwaitinfo().

Новое в версии 3.3.

Изменено в версии 3.5: Теперь функция повторяется с пересчитанным timeout, если прервана сигналом, не входящим в sigset, и обработчик сигнала не вызывает исключение (обоснование см. в PEP 475).

18.8.3. Пример

Вот минимальный пример программы. В ней используется функция alarm() для ограничения времени ожидания открытия файла; это полезно, если файл относится к последовательному устройству, которое может быть не включено, что обычно приводит к бесконечной блокировке os.open(). Решение – установить 5-секундный будильник перед открытием файла; если операция занимает слишком много времени, будет отправлен сигнал будильника, и обработчик вызовет исключение.

python
import signal, os

def handler(signum, frame):
    print('Signal handler called with signal', signum)
    raise OSError("Couldn't open device!")

# Установить обработчик сигнала и будильник на 5 секунд
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)

# Этот вызов open() может зависнуть на неопределенный срок
fd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0)          # Отключить будильник