Spec-Zone.ru › Python 3.10

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.

END_OF_DOCUMENT_MARKER
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.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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API