сигнал — Установка обработчиков для асинхронных событий
Исходный код: Lib/signal.py
Этот модуль предоставляет механизмы использования обработчиков сигналов в Python.
Общие правила
Функция signal.signal() позволяет определять пользовательские обработчики, которые будут выполняться при получении сигнала. Установлено небольшое количество стандартных обработчиков: SIGPIPE игнорируется (чтобы ошибки записи в каналы и сокеты могли быть отображены как обычные исключения Python) и SIGINT преобразуется в исключение KeyboardInterrupt, если родительский процесс его не изменил.
Обработчик для конкретного сигнала, после установки, остается установленным, пока он явно не будет сброшен (Python эмулирует интерфейс стиля BSD независимо от реализации), за исключением обработчика для SIGCHLD, который следует реализации.
На платформах WebAssembly wasm32-emscripten и wasm32-wasi, сигналы эмулируются и поэтому ведут себя по-другому. Несколько функций и сигналов недоступны на этих платформах.
Выполнение обработчиков сигналов 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 (Signals, Handlers и Sigmasks соответственно). getsignal(), pthread_sigmask(), sigpending() и sigwait() функции возвращают удобочитаемые enums как Signals объекты.
Модуль signal определяет три перечисления:
-
class signal.Signals -
enum.IntEnumколлекция констант SIG* и констант CTRL_*.Новое в версии 3.5.
-
class signal.Handlers -
enum.IntEnumколлекция константSIG_DFLиSIG_IGN.Новое в версии 3.5.
-
class signal.Sigmasks -
enum.IntEnumколлекция константSIG_BLOCK,SIG_UNBLOCKиSIG_SETMASK.Доступность: Unix.
См. страницу руководства sigprocmask(2) и pthread_sigmask(3) для получения дополнительной информации.
Новое в версии 3.5.
Переменные, определённые в модуле signal:
-
signal.SIG_DFL -
Это один из двух стандартных вариантов обработки сигналов; он просто выполнит стандартную функцию для сигнала. Например, на большинстве систем стандартное действие для
SIGQUIT— это выброс core и выход, а стандартное действие для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.Доступность: не macOS.
-
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.SIGSTKFLT -
Ошибка стека сопроцессора. Ядро Linux не генерирует этот сигнал: его можно генерировать только в пользовательском пространстве.
Доступность: Linux.
На архитектурах, где сигнал доступен. Смотрите страницу руководства signal(7) для получения дополнительной информации.
Новое в версии 3.11.
-
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 -
На единицу больше, чем наибольший номер сигнала. Для получения допустимых номеров сигналов используйте
valid_signals().
-
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, например, «Прерывание» для
SIGINT. ВозвращаетNone, если для signalnum нет описания. ВызываетValueError, если signalnum недействителен.Добавлена в версии 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.Старые значения возвращаются как кортеж: (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, когда сигналы теряются.Во втором подходе мы используем 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, равным 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. Обратите внимание, что не все системы определяют один и тот же набор имён сигналов; если имя сигнала не определено как константа уровня модуляSIG*, будет возбуждено исключениеAttributeError.
-
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):
signame = signal.Signals(signum).name
print(f'Signal handler called with signal {signame} ({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.11/library/signal.html