signal — Установка обработчиков асинхронных событий
Этот модуль предоставляет механизмы для использования обработчиков сигналов в 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) и маски сигналов (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, то сигнал не отправляется, но проверка ошибок всё равно выполняется; это можно использовать для проверки того, всё ли в порядке с целевым потоком.
Вызывает событие аудита auditing event
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.Возвращаются старые значения в виде кортежа: (delay, interval).
Попытка передать некорректный таймер интервала вызовет
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 occcurs 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–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.9/library/signal.html