signal — Установка обработчиков асинхронных событий
Исходный код: Lib/signal.py
Этот модуль предоставляет механизмы для использования обработчиков сигналов в Python.
Общие правила
Функция signal.signal() позволяет определять пользовательские обработчики, которые будут выполняться при получении сигнала. Установлено небольшое количество обработчиков по умолчанию: SIGPIPE игнорируется (чтобы ошибки записи в каналы и сокеты могли быть сообщены как обычные исключения 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.
Модуль 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.pidfd_send_signal(pidfd, sig, siginfo=None, flags=0) -
Отправляет сигнал sig процессу, указанному дескриптором файла pidfd. Python в настоящее время не поддерживает параметр siginfo; он должен быть
None. Аргумент flags предназначен для будущих расширений; в настоящее время значения флагов не определены.См. страницу руководства pidfd_send_signal(2) для получения дополнительной информации.
Доступность: Linux 5.1+
Новая в версии 3.9.
-
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(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, для срабатывания через seconds (принят float, отличается от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. Это можно использовать библиотеке для пробуждения вызовов poll или select, позволяя полностью обработать сигнал.
Возвращается старый дескриптор файла пробуждения (или -1, если пробуждение по дескриптору файла не было включено). Если fd равно -1, пробуждение по дескриптору файла отключено. Если не -1, fd должен быть без блокировки. Библиотека должна удалить любые байты из fd перед повторным вызовом poll или select.
При включённых потоках эту функцию можно вызывать только из главного потока основного интерпретатора; попытка вызвать её из других потоков вызовет исключение
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, в противном случае системные вызовы будут прерываться. Возвращает ничего.Доступность: Unix. См. страницу руководства siginterrupt(3) для получения дополнительной информации.
Обратите внимание, что установка обработчика сигналов с помощью
signal()сбросит поведение перезапуска на прерывание, неявно вызвавsiginterrupt()со значением flag, равным true, для данного сигнала.
-
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 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. Это приведёт к неожиданному завершению программы всякий раз, когда соединение сокета прерывается, пока программа по-прежнему записывает в него.
Замечание об обработчиках сигналов и исключениях
Если обработчик сигнала вызывает исключение, исключение будет передано в основной поток и может быть вызвано после любой инструкции байт-кода. В частности, исключение 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...")
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/signal.html