Модуль signal
предоставляет механизмы для использования обработчиков сигналов в Python.
Сигналы - это функция операционной системы, которая позволяет уведомлять программу о событии и обрабатывать его асинхронно. Они могут быть созданы самой системой или отправлены из одного процесса в другой. Поскольку сигналы прерывают регулярный поток программы, возможно, что некоторые операции, особенно ввод/вывод, могут привести к ошибкам, если сигнал принимается посередине.
Как и в других формах программирования, основанного на событиях, сигналы принимаются путем создания функции обратного вызова, называемой обработчиком сигнала, которая вызывается при возникновении сигнала. Аргументами обработчика сигнала являются номер сигнала и кадр стека от точки в программе, которая была прервана сигналом.
Функция signal.signal()
позволяет определять пользовательские обработчики, которые будут выполняться при получении сигнала. Установлено небольшое количество обработчиков по умолчанию: signal.SIGPIPE
игнорируется, поэтому об ошибках записи в каналах и сокетах можно сообщать как обычные исключения Python, а signal.SIGINT
преобразуется в исключение KeyboardInterrupt
, если родительский процесс не изменил его.
Обработчик определенного сигнала после его установки остается до тех пор, пока он не будет сброшен явным образом, за исключением обработчика для signal.SIGCHLD
, который следует за базовой реализацией.
Обработчик сигналов Python не выполняется внутри обработчика сигналов низкого уровня (C). Вместо него, низкоуровневый обработчик сигнала устанавливает флаг, который указывает виртуальной машине выполнить соответствующий обработчик сигнала Python на более позднем этапе, например при следующей инструкции байт-кода.
Это имеет последствия:
signal.SIGFPE
или signal.SIGSEGV
, которые вызваны недопустимой операцией в C-коде. Python вернется из обработчика сигнала к коду C, который, вероятно, снова вызовет тот же сигнал, что приведет к зависанию Python. Начиная с Python 3.3 можно использовать модуль обработчика ошибок, чтобы сообщать о синхронных ошибках.Обработчики сигналов Python всегда выполняются в основном потоке Python, даже если сигнал был получен в другом потоке. Это означает, что сигналы не могут использоваться в качестве средства связи между потоками. Вместо этого вы можете использовать примитивы синхронизации из модуля threading
.
Кроме того, только основной поток может устанавливать новый обработчик сигнала.
Примечание:
Пример программы, которая использует функцию OS signal.alarm()
, чтобы ограничить время ожидания открытия файла. Такое поведение полезно, если файл предназначен для последовательного устройства, которое может не включаться, что приводит к зависанию функции os.open()
на неопределенное время. Решение состоит в том, чтобы установить 5-секундную тревогу перед открытием файла. Если операция занимает слишком много времени, сигнал тревоги будет отправлен, и обработчик вызывает исключение.
import signal, os def handler_alarm(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_alarm) signal.alarm(5) # This open() may hang indefinitely fd = os.open('/dev/ttyS0', os.O_RDWR) # Отключить alarm signal.alarm(0)
Передача выходных данных программы на такие инструменты, как head(1)
, приведет к отправке сигнала signal.SIGPIPE
вашему процессу, когда приемник стандартного выхода закроется раньше. Это приводит к исключению, подобному BrokenPipeError: [Errno 32] Broken pipe
. Чтобы поймать это исключение, оберните точку входа следующим образом:
import os, sys def main(): try: # Симулировать большой вывод # (замените этот цикл на ваш код) for x in range(10000): print("y") # здесь сбросим вывод, чтобы запустить # внутри блока try - SIGPIPE. sys.stdout.flush() except BrokenPipeError: # Python очищает стандартные потоки при выходе. # Перенаправим оставшийся вывод в 'devnull', чтобы # избежать другого 'BrokenPipeError' при завершении работы. devnull = os.open(os.devnull, os.O_WRONLY) os.dup2(devnull, sys.stdout.fileno()) # Python выходит с кодом ошибки 1 в EPIPE sys.exit(1) if __name__ == '__main__': main()
Не устанавливайте SIGPIPE в SIG_DFL, чтобы избежать исключения BrokenPipeError
. Это может привести к неожиданному завершению работы программы, также при каждом прерывании любого соединения с сокетом, когда программа все еще выполняет запись в него.