signal — Устанавливает обработчики асинхронных событий


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

Основные правила

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

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

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

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

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

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

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

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

Содержание модуля

Изменено в версии 3.5: сигнал (SIG*), обработчик (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

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

signal.SIGABRT

Сигнал отмены от abort(3).

signal.SIGALRM

Сигнал таймера от alarm(2).

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

signal.SIGBREAK

Прерывание с клавиатуры (CTRL + BREAK).

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

signal.SIGBUS

Ошибка шины (плохой доступ к памяти).

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

signal.SIGCHLD

Дочерний процесс остановлен или завершён.

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

signal.SIGCLD

Псевдоним SIGCHLD.

signal.SIGCONT

Продолжить процесс, если он в данный момент остановлен.

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

signal.SIGFPE

Исключение с плавающей запятой. Например, деление на ноль.

См.также

ZeroDivisionError вызывается, когда второй аргумент деления или операции по модулю равен нулю.

signal.SIGHUP

Обнаружено зависание на управляющем терминале или смерть управляющего процесса.

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

signal.SIGILL

Незаконный оператор.

signal.SIGINT

Прерывание с клавиатуры (CTRL + C).

Действие по умолчанию — вызов KeyboardInterrupt.

signal.SIGKILL

Сигнал убийства.

Его нельзя поймать, заблокировать или проигнорировать.

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

signal.SIGPIPE

Сломанная конвейер: запись в конвейер без считывателей.

Действие по умолчанию — игнорировать сигнал.

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

signal.SIGSEGV

Ошибка сегментации: неверная ссылка на память.

signal.SIGTERM

Сигнал завершения.

signal.SIGUSR1

Пользовательский сигнал 1.

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

signal.SIGUSR2

Пользовательский сигнал 2.

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

signal.SIGWINCH

Сигнал изменения размера окна.

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

SIG*

Все номера сигналов определены символически. Например, сигнал отбоя определяется как 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 определяет одно исключение:

exception signal.ItimerError

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

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

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

signal.alarm(time)

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

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

signal.getsignal(signalnum)

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

signal.strsignal(signalnum)

Возвращает системное описание сигнала signalnum, например «Прерывание», «Ошибка сегментации» и т. д. Возвращает None, если сигнал не распознан.

Добавлено в версии 3.8.

signal.valid_signals()

Возвращает множество допустимых номеров сигналов на этой платформе. Это значение может быть меньше range(1, NSIG), если некоторые сигналы зарезервированы системой для внутреннего использования.

Добавлено в версии 3.8.

signal.pause()

Заставляет процесс спать, пока не будет получен сигнал; затем будет вызван соответствующий обработчик. Ничего не возвращает.

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

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

signal.raise_signal(signum)

Посылает сигнал вызывающему процессу. Ничего не возвращает.

Добавлено в версии 3.8.

signal.pthread_kill(thread_id, signalnum)

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

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

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

Вызывает событие аудита signal.pthread_kill с аргументами thread_id, signalnum.

Доступность: 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}). Используйте valid_signals() для полной маски, включая все сигналы.

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

SIGKILL и SIGSTOP не могут быть заблокированы.

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

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

Добавлено в версии 3.3.

signal.setitimer(which, seconds, interval=0.0)

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

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

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

Попытка передать недопустимый интервальный таймер приведёт к ошибке ItimerError.

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

signal.getitimer(which)

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

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

signal.set_wakeup_fd(fd, *, warn_on_full_buffer=True)

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

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

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

Существует два распространенных способа использования этой функции. В обоих подходах вы используете fd для пробуждения при поступлении сигнала, но затем они различаются тем, как они определяют сигнал which или поступление сигналов.

При первом подходе мы считываем данные из буфера fd, а значения байтов дают вам номера сигналов. Это просто, но в редких случаях это может привести к проблемам: обычно fd имеет ограниченный объём буферного пространства, и если слишком много сигналов поступает слишком быстро, то буфер может переполниться, и некоторые сигналы могут быть потеряны. Если вы используете данный подход, вы должны установить warn_on_full_buffer=True, что по крайней мере приведёт к выводу предупреждения в stderr при потере сигналов.

Во втором подходе мы используем wakeup fd только для пробуждения и игнорируем фактические значения байтов. В этом случае все, о чем мы заботимся, это является ли буфер fd пустым или непустым; полный буфер вообще не указывает на проблему. Если вы используете данный подход, вы должны установить warn_on_full_buffer=False, чтобы ваши пользователи не были сбиты с толку ложными предупреждающими сообщениями.

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

Изменено в версии 3.7: Добавлен параметр warn_on_full_buffer.

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. См. справочную страницу sigtimedwait(2) для получения дополнительной информации.

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

Добавлено в версии 3.3.

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

Пример

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

import signal, os

def handler(signum, frame):
    print('Обработчик сигнала вызывается с помощью signal', signum)
    raise OSError("Не удалось открыть устройство!")

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

# Tего open() может зависать бесконечно
fd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0)          # Отключает сигнализацию

Примечание по SIGPIPE

Передача вывода вашей программы на такие инструменты, как head(1), приведёт к отправке сигнала SIGPIPE в ваш процесс, когда приемник его стандартного вывода закрывается раньше времени. Это приводит к исключению типа BrokenPipeError: [Errno 32] Broken pipe. Чтобы справиться с этим случаем, оборачивает точку входа, чтобы перехватить это исключение, следующим образом:

import os
import sys

def main():
    try:
        # имитирует большой вывод (ваш код заменяет этот цикл)
        for x in range(10000):
            print("y")
        # сбросывает вывод здесь, чтобы принудительно запустить SIGPIPE
        # находясь внутри этого блока try.
        sys.stdout.flush()
    except BrokenPipeError:
        # Python сбрасывает стандартные потоки при выходе; перенаправляет оставшийся вывод
        # к dev null, чтобы избежать еще одной ошибки прерывания конвейера при завершении работы
        devnull = os.open(os.devnull, os.O_WRONLY)
        os.dup2(devnull, sys.stdout.fileno())
        sys.exit(1)  # Python завершает работу с кодом ошибки 1 в EPIPE

if __name__ == '__main__':
    main()

Не устанавливайте расположение SIGPIPE в SIG_DFL, чтобы избежать BrokenPipeError. Это приведёт к неожиданному выходу вашей программы также всякий раз, когда любое соединение сокета прерывается, пока ваша программа все ещё записывает в него.