signal
— Установка обработчиков для асинхронных событий¶
Этот модуль предоставляет механизмы для использования обработчиков сигналов в Python.
Общие правила¶
Функция signal.signal()
позволяет определить пользовательские обработчики, которые будут выполняться при получении сигнала. Установлено небольшое количество обработчиков по умолчанию: SIGPIPE
игнорируется (поэтому ошибки записи по трубам и сокетам могут сообщаться как обычные исключения Python), а SIGINT
переводится в исключение KeyboardInterrupt
, если родительский процесс не изменил его.
Обработчик определенного сигнала, будучи установленным, остается установленным до тех пор, пока его явно не сбросят (Python эмулирует интерфейс в стиле BSD независимо от базовой реализации), за исключением обработчика для SIGCHLD
, который следует базовой реализации.
Выполнение обработчиков сигналов Python¶
Обработчик сигналов Python не выполняется внутри низкоуровневого (C) обработчика сигналов. Вместо этого низкоуровневый обработчик сигнала устанавливает флаг, который указывает программе virtual machine выполнить соответствующий обработчик сигнала Python в более поздний момент (например, при выполнении следующей инструкции bytecode). Это имеет свои последствия:
Не имеет смысла ловить синхронные ошибки типа
SIGFPE
илиSIGSEGV
, которые вызваны недопустимой операцией в коде на Си. Python вернется из обработчика сигнала в Си-код, который, скорее всего, снова поднимет тот же сигнал, что приведет к зависанию Python. Начиная с Python 3.3, для сообщения о синхронных ошибках можно использовать модульfaulthandler
.Длительное вычисление, реализованное исключительно на C (например, поиск регулярного выражения в большом тексте), может выполняться непрерывно в течение произвольного количества времени, независимо от любых полученных сигналов. Обработчики сигналов Python будут вызваны по завершении вычисления.
Если обработчик вызывает исключение, оно будет вызвано «из воздуха» в главном потоке. См. обсуждение в note below.
Сигналы и нити¶
Обработчики сигналов Python всегда выполняются в основном потоке Python главного интерпретатора, даже если сигнал был получен в другом потоке. Это означает, что сигналы нельзя использовать как средство межпоточного взаимодействия. Вместо этого вы можете использовать примитивы синхронизации из модуля threading
.
Кроме того, только главному потоку основного интерпретатора разрешено устанавливать новый обработчик сигналов.
Содержание модуля¶
Изменено в версии 3.5: Константы, связанные с сигналом (SIG*), обработчиком (SIG_DFL
, SIG_IGN
) и сигмаской (SIG_BLOCK
, SIG_UNBLOCK
, SIG_SETMASK
), перечисленные ниже, были преобразованы в enums
. Функции getsignal()
, pthread_sigmask()
, sigpending()
и sigwait()
возвращают человекочитаемые enums
.
Переменными, определенными в модуле signal
, являются:
-
signal.
SIG_DFL
¶ Это один из двух стандартных вариантов обработки сигнала; он просто выполняет функцию по умолчанию для данного сигнала. Например, в большинстве систем стандартным действием для сигнала
SIGQUIT
является дамп ядра и выход, а стандартным действием для сигналаSIGCHLD
является его игнорирование.
-
signal.
SIG_IGN
¶ Это еще один стандартный обработчик сигналов, который будет просто игнорировать данный сигнал.
-
signal.
SIGALRM
¶ Сигнал таймера от alarm(2).
Availability: Unix.
-
signal.
SIGBREAK
¶ Прерывание с клавиатуры (CTRL + BREAK).
Availability: Windows.
-
signal.
SIGBUS
¶ Ошибка шины (плохой доступ к памяти).
Availability: Unix.
-
signal.
SIGCHLD
¶ Детский процесс остановлен или завершен.
Availability: Unix.
-
signal.
SIGCONT
¶ Продолжить процесс, если он в данный момент остановлен
Availability: Unix.
-
signal.
SIGFPE
¶ Исключение с плавающей точкой. Например, деление на ноль.
См.также
ZeroDivisionError
возникает, когда второй аргумент операции деления или модуляции равен нулю.
-
signal.
SIGHUP
¶ Обнаружено зависание на управляющем терминале или завершение управляющего процесса.
Availability: Unix.
-
signal.
SIGILL
¶ Незаконная инструкция.
-
signal.
SIGINT
¶ Прерывание с клавиатуры (CTRL + C).
Действие по умолчанию - поднять
KeyboardInterrupt
.
-
signal.
SIGKILL
¶ Сигнал убийства.
Его нельзя поймать, заблокировать или игнорировать.
Availability: Unix.
-
signal.
SIGPIPE
¶ Сломанная труба: писать на трубу без читателей.
Действие по умолчанию - игнорировать сигнал.
Availability: Unix.
-
signal.
SIGSEGV
¶ Ошибка сегментации: недопустимая ссылка на память.
-
signal.
SIGTERM
¶ Сигнал окончания.
-
signal.
SIGUSR1
¶ Определяемый пользователем сигнал 1.
Availability: Unix.
-
signal.
SIGUSR2
¶ Определяемый пользователем сигнал 2.
Availability: Unix.
-
signal.
SIGWINCH
¶ Сигнал изменения размера окна.
Availability: Unix.
-
SIG*
Все номера сигналов определяются символически. Например, сигнал зависания определяется как
signal.SIGHUP
; имена переменных идентичны именам, используемым в программах на языке Си, как указано в<signal.h>
. На странице Unix man для „signal()
“ перечислены существующие сигналы (в некоторых системах это signal(2), в других список находится в signal(7)). Обратите внимание, что не все системы определяют одинаковый набор имен сигналов; только те имена, которые определены системой, определяются этим модулем.
-
signal.
CTRL_C_EVENT
¶ Сигнал, соответствующий событию нажатия клавиши Ctrl+C. Этот сигнал можно использовать только с
os.kill()
.Availability: Windows.
Добавлено в версии 3.2.
-
signal.
CTRL_BREAK_EVENT
¶ Сигнал, соответствующий событию нажатия клавиши Ctrl+Break. Этот сигнал можно использовать только с
os.kill()
.Availability: 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
.
Модуль signal
определяет следующие функции:
-
signal.
alarm
(time)¶ Если time ненулевое, то эта функция запрашивает, чтобы через time секунд процессу был послан сигнал
SIGALRM
. Любой ранее запланированный сигнал отменяется (в любой момент времени может быть запланирован только один сигнал). Возвращаемое значение - это количество секунд до того, как должен был быть подан любой ранее установленный сигнал тревоги. Если time равно нулю, сигнал тревоги не запланирован, а любой запланированный сигнал тревоги отменяется. Если возвращаемое значение равно нулю, то ни один сигнал тревоги в данный момент не запланирован.Availability: Unix. Дополнительную информацию см. на странице man 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
()¶ Вызывает процесс, который будет спать до тех пор, пока не будет получен сигнал; затем будет вызван соответствующий обработчик. Ничего не возвращает.
Availability: Unix. Дополнительную информацию см. на странице man signal(2).
См. также
sigwait()
,sigwaitinfo()
,sigtimedwait()
иsigpending()
.
-
signal.
raise_signal
(signum)¶ Посылает сигнал вызывающему процессу. Ничего не возвращает.
Добавлено в версии 3.8.
-
signal.
pidfd_send_signal
(pidfd, sig, siginfo=None, flags=0)¶ Отправьте сигнал sig процессу, на который ссылается файловый дескриптор pidfd. В настоящее время Python не поддерживает параметр siginfo; он должен быть
None
. Аргумент flags предусмотрен для будущих расширений; в настоящее время значения флагов не определены.Дополнительную информацию см. на странице руководства pidfd_send_signal(2).
Availability: Linux 5.1+
Добавлено в версии 3.9.
-
signal.
pthread_kill
(thread_id, signalnum)¶ Отправьте сигнал signalnum в поток thread_id, другой поток в том же процессе, что и вызывающая сторона. Целевой поток может выполнять любой код (Python или нет). Однако, если целевой поток выполняет интерпретатор Python, обработчики сигналов Python будут executed by the main thread of the main interpreter. Поэтому единственный смысл посылать сигнал конкретному потоку Python заключается в том, чтобы заставить запущенный системный вызов завершиться с ошибкой
InterruptedError
.Используйте
threading.get_ident()
или атрибутident
объектовthreading.Thread
для получения подходящего значения thread_id.Если signalnum равен 0, то сигнал не посылается, но проверка ошибок все равно выполняется; это можно использовать для проверки того, что целевой поток все еще запущен.
Вызывает auditing event
signal.pthread_kill
с аргументамиthread_id
,signalnum
.Availability: Unix. Дополнительную информацию см. на странице man 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
не могут быть заблокированы.Availability: Unix. Дополнительную информацию см. на странице man sigprocmask(2) и 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, на срабатывание через секунды (принимается float, отличается отalarm()
) и после этого каждые интервал секунд (если интервал ненулевой). Интервальный таймер, указанный which, может быть очищен установкой seconds в ноль.Когда срабатывает интервальный таймер, процессу посылается сигнал. Посылаемый сигнал зависит от используемого таймера;
signal.ITIMER_REAL
передастSIGALRM
,signal.ITIMER_VIRTUAL
пошлетSIGVTALRM
, аsignal.ITIMER_PROF
передастSIGPROF
.Старые значения возвращаются в виде кортежа: (задержка, интервал).
Попытка передать недопустимый таймер интервала приведет к ошибке
ItimerError
.Availability: Unix.
-
signal.
getitimer
(which)¶ Возвращает текущее значение заданного интервального таймера, указанного which.
Availability: Unix.
-
signal.
set_wakeup_fd
(fd, *, warn_on_full_buffer=True)¶ Установите дескриптор файла пробуждения в fd. Когда сигнал получен, номер сигнала записывается в fd как один байт. Это может быть использовано библиотекой для пробуждения вызова опроса или выбора, позволяя сигналу быть полностью обработанным.
Возвращается старое значение fd пробуждения (или -1, если пробуждение файлового дескриптора не было включено). Если fd равно -1, пробуждение файлового дескриптора отключено. Если не -1, fd должен быть неблокирующим. Библиотека сама должна удалить все байты из fd перед повторным вызовом poll или select.
Когда потоки включены, эта функция может быть вызвана только из the main thread of the main interpreter; попытка вызвать ее из других потоков вызовет исключение
ValueError
.Существует два распространенных способа использования этой функции. В обоих подходах вы используете fd для пробуждения при поступлении сигнала, но они отличаются тем, как они определяют какой сигнал или сигналы поступили.
При первом подходе мы считываем данные из буфера fd, а значения байтов дают вам номера сигналов. Это просто, но в редких случаях может возникнуть проблема: обычно у fd ограниченный объем буфера, и если слишком много сигналов поступает слишком быстро, то буфер может переполниться, и некоторые сигналы могут быть потеряны. Если вы используете этот подход, то вам следует установить
warn_on_full_buffer=True
, что, по крайней мере, приведет к печати предупреждения на stderr при потере сигналов.При втором подходе мы используем пробуждающий fd только для пробуждения и игнорируем фактические значения байтов. В этом случае нас интересует только то, пуст или непуст буфер fd; полный буфер вообще не указывает на проблему. Если вы используете этот подход, то вам следует установить
warn_on_full_buffer=False
, чтобы ваши пользователи не были сбиты с толку ложными предупреждающими сообщениями.Изменено в версии 3.5: В Windows эта функция теперь также поддерживает ручки сокетов.
Изменено в версии 3.7: Добавлен параметр
warn_on_full_buffer
.
-
signal.
siginterrupt
(signalnum, flag)¶ Изменяет поведение перезапуска системных вызовов: если flag равен
False
, системные вызовы будут перезапущены, когда их прервет сигнал signalnum, в противном случае системные вызовы будут прерваны. Ничего не возвращает.Availability: Unix. Дополнительную информацию см. на странице man siginterrupt(3).
Обратите внимание, что установка обработчика сигнала с помощью
signal()
приведет к сбросу поведения перезапуска на прерывистое путем неявного вызоваsiginterrupt()
с истинным значением флага для данного сигнала.
-
signal.
signal
(signalnum, handler)¶ Установите обработчик сигнала signalnum на функцию handler. handler может быть вызываемым объектом Python, принимающим два аргумента (см. ниже), или одним из специальных значений
signal.SIG_IGN
илиsignal.SIG_DFL
. Будет возвращен предыдущий обработчик сигнала (см. описаниеgetsignal()
выше). (Дополнительную информацию см. на странице Unix man signal(2)).Когда потоки включены, эта функция может быть вызвана только из the main thread of the main interpreter; попытка вызвать ее из других потоков вызовет исключение
ValueError
.Вызывается handler с двумя аргументами: номер сигнала и текущий фрейм стека (
None
или объект фрейма; описание объектов фрейма см. в description in the type hierarchy или см. описание атрибутов в модулеinspect
).В Windows,
signal()
может быть вызвано только сSIGABRT
,SIGFPE
,SIGILL
,SIGINT
,SIGSEGV
,SIGTERM
илиSIGBREAK
. В любом другом случае будет выдано сообщениеValueError
. Обратите внимание, что не все системы определяют одинаковый набор имен сигналов; будет выдано сообщениеAttributeError
, если имя сигнала не определено какSIG*
константа уровня модуля.
-
signal.
sigpending
()¶ Изучите набор сигналов, которые ожидают доставки вызывающему потоку (т.е. сигналы, которые были подняты во время блокировки). Возвращает набор ожидающих сигналов.
Availability: Unix. Дополнительную информацию см. на странице man sigpending(2).
См. также
pause()
,pthread_sigmask()
иsigwait()
.Добавлено в версии 3.3.
-
signal.
sigwait
(sigset)¶ Приостановить выполнение вызывающего потока до получения одного из сигналов, указанных в наборе сигналов sigset. Функция принимает сигнал (удаляет его из списка ожидающих сигналов) и возвращает номер сигнала.
Availability: Unix. Дополнительную информацию см. на странице man 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
.Availability: Unix. Дополнительную информацию см. на странице man sigwaitinfo(2).
См. также
pause()
,sigwait()
иsigtimedwait()
.Добавлено в версии 3.3.
Изменено в версии 3.5: Теперь функция повторяется, если прервана сигналом, не входящим в sigset, и обработчик сигнала не вызывает исключения (см. обоснование в PEP 475).
-
signal.
sigtimedwait
(sigset, timeout)¶ Аналогично
sigwaitinfo()
, но принимает дополнительный аргумент timeout, задающий тайм-аут. Если timeout указан как0
, выполняется опрос. ВозвращаетNone
, если произошел тайм-аут.Availability: Unix. Дополнительную информацию см. на странице man 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 handler called with signal', signum)
raise OSError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable the alarm
Примечание по SIGPIPE¶
Передача вывода вашей программы в инструменты типа head(1) приведет к тому, что сигнал SIGPIPE
будет послан вашему процессу, когда приемник его стандартного вывода закроется раньше времени. Это приведет к исключению типа BrokenPipeError: [Errno 32] Broken pipe
. Чтобы справиться с этим случаем, оберните вашу точку входа для перехвата этого исключения следующим образом:
import os
import sys
def main():
try:
# simulate large output (your code replaces this loop)
for x in range(10000):
print("y")
# flush output here to force SIGPIPE to be triggered
# while inside this try block.
sys.stdout.flush()
except BrokenPipeError:
# Python flushes standard streams on exit; redirect remaining output
# to devnull to avoid another BrokenPipeError at shutdown
devnull = os.open(os.devnull, os.O_WRONLY)
os.dup2(devnull, sys.stdout.fileno())
sys.exit(1) # Python exits with error code 1 on EPIPE
if __name__ == '__main__':
main()
Не устанавливайте диспозицию SIGPIPE
в SIG_DFL
, чтобы избежать BrokenPipeError
. Это приведет к неожиданному завершению работы вашей программы при прерывании любого сокетного соединения, пока ваша программа продолжает писать в него.
Примечание об обработчиках сигналов и исключениях¶
Если обработчик сигнала вызывает исключение, оно будет передано в основной поток и может быть вызвано после любой инструкции bytecode. В частности, KeyboardInterrupt
может появиться в любой момент выполнения. Большинство кода Python, включая стандартную библиотеку, не может быть защищено от этого, и поэтому KeyboardInterrupt
(или любое другое исключение, вызванное обработчиком сигнала) в редких случаях может привести программу в неожиданное состояние.
Чтобы проиллюстрировать этот вопрос, рассмотрим следующий код:
class SpamContext:
def __init__(self):
self.lock = threading.Lock()
def __enter__(self):
# If KeyboardInterrupt occurs here, everything is fine
self.lock.acquire()
# If KeyboardInterrupt occurs here, __exit__ will not be called
...
# KeyboardInterrupt could occur just before the function returns
def __exit__(self, exc_type, exc_val, exc_tb):
...
self.lock.release()
Для многих программ, особенно тех, которые просто хотят завершить работу при KeyboardInterrupt
, это не проблема, но приложениям, которые сложны или требуют высокой надежности, следует избегать вызывать исключения из обработчиков сигналов. Они также должны избегать перехвата KeyboardInterrupt
как средства изящного завершения работы. Вместо этого им следует установить свой собственный обработчик SIGINT
. Ниже приведен пример HTTP-сервера, который избегает KeyboardInterrupt
:
import signal
import socket
from selectors import DefaultSelector, EVENT_READ
from http.server import HTTPServer, SimpleHTTPRequestHandler
interrupt_read, interrupt_write = socket.socketpair()
def handler(signum, frame):
print('Signal handler called with signal', signum)
interrupt_write.send(b'\0')
signal.signal(signal.SIGINT, handler)
def serve_forever(httpd):
sel = DefaultSelector()
sel.register(interrupt_read, EVENT_READ)
sel.register(httpd, EVENT_READ)
while True:
for key, _ in sel.select():
if key.fileobj == interrupt_read:
interrupt_read.recv(1)
return
if key.fileobj == httpd:
httpd.handle_request()
print("Serving on port 8000")
httpd = HTTPServer(('', 8000), SimpleHTTPRequestHandler)
serve_forever(httpd)
print("Shutdown...")