Сигналы - это ограниченная форма inter -процессная связь (IPC), обычно используется в Unix, Unix-like и других POSIX -совместимых операционных системах. Сигнал - это асинхронное уведомление, отправленное процессу или конкретному потоку в том же процессе, чтобы уведомить его о произошедшем событии. Сигналы возникли в 1970-х годах Bell Labs Unix и недавно были определены в стандарте POSIX.
При отправке сигнала операционная система прерывает обычный поток выполнения целевого процесса, чтобы доставить сигнал. Выполнение может быть прервано любой неатомарной инструкцией. Если процесс ранее зарегистрировал обработчик сигнала, эта процедура выполняется. В противном случае выполняется обработчик сигнала по умолчанию.
Встроенные программы могут находить сигналы полезными для межпроцессного взаимодействия, поскольку объем вычислений и памяти для сигналов невелик.
Сигналы аналогичны прерываниям, разница в том, что прерывания опосредуются процессором и обрабатываются ядром, в то время как сигналы опосредуются ядром (возможно, через систему вызовы) и обрабатываются процессами. Ядро может передать прерывание в качестве сигнала вызвавшему его процессу (типичные примеры: SIGSEGV, SIGBUS, SIGILL и SIGFPE).
Версия 1 Unix имела отдельные системные вызовы для перехвата прерываний, завершения и обработки ловушки. Версия 4 объединила все ловушки в один вызов, сигнал, и каждая пронумерованная ловушка получила символическое имя в Версии 7. kill появился в версии 2, а в версии 5 мог посылать произвольные сигналы. Plan 9 от Bell Labs заменил сигналы заметками, которые позволяют отправлять короткие, произвольные строки.
Системный вызов kill (2) отправляет указанный сигнал указанному процессу, если позволяют разрешения. Точно так же команда kill (1) позволяет пользователю отправлять сигналы процессам. Библиотечная функция raise (3) отправляет указанный сигнал текущему процессу.
Исключения, такие как деление на ноль или нарушение сегментации, будут генерировать сигналы (здесь SIGFPE «исключение с плавающей запятой» и SIGSEGV «нарушение сегментации» соответственно, которые по умолчанию вызывают дамп ядра и выход из программы).
Ядро может генерировать сигналы для уведомления процессов о событиях. Например, SIGPIPE будет сгенерирован, когда процесс записывает в канал, который был закрыт устройством чтения; по умолчанию это приводит к завершению процесса, что удобно при построении конвейеров оболочки.
Ввод определенных комбинаций клавиш на управляющем терминале запущенного процесса заставляет систему отправлять ему определенные сигналы:
Эти комбинации клавиш по умолчанию в современных операционных системах можно изменить с помощью команды stty.
Обработчики сигналов могут быть установлены с помощью системного вызова signal (2) или sigaction (2). Если обработчик сигнала не установлен для определенного сигнала, используется обработчик по умолчанию. В противном случае сигнал перехватывается, и вызывается обработчик сигнала. Процесс также может указать два поведения по умолчанию без создания обработчика: игнорировать сигнал (SIG_IGN) и использовать обработчик сигнала по умолчанию (SIG_DFL). Есть два сигнала, которые нельзя перехватить и обработать: SIGKILL и SIGSTOP.
Обработка сигналов уязвима для состояний гонки. Поскольку сигналы являются асинхронными, другой сигнал (даже того же типа) может быть доставлен процессу во время выполнения процедуры обработки сигналов.
Вызов sigprocmask (2) может использоваться для блокировки и разблокировки доставки сигналов. Заблокированные сигналы не доставляются в процесс, пока не будут разблокированы. Сигналы, которые нельзя игнорировать (SIGKILL и SIGSTOP), нельзя заблокировать.
Сигналы могут вызывать прерывание текущего системного вызова, оставляя управление непрозрачным перезапуском.
Обработчики сигналов должны быть написаны таким образом, чтобы это не приводило к нежелательные побочные эффекты, например изменение ошибки, изменение маски сигнала, изменение расположения сигнала и другие изменения атрибутов глобального процесса . Использование не реентерабельных функций, например, malloc или printf, внутри обработчиков сигналов также небезопасно. В частности, спецификация POSIX и справочная страница Linux signal (7) требуют, чтобы все системные функции, прямо или косвенно вызываемые из сигнальной функции, были безопасными для асинхронных сигналов. signal-safety (7) дает список таких функций системы async-signal safe (практически системные вызовы ), в противном случае это неопределенное поведение. Предлагается просто установить некоторую volatile sig_atomic_t
переменную в обработчике сигнала и протестировать ее где-нибудь еще.
Обработчики сигналов могут вместо этого поместить сигнал в очередь и немедленно вернуться. Затем основной поток будет продолжать "непрерывно" до тех пор, пока из очереди не будут приняты сигналы, например, в цикле событий . «Непрерывный» здесь означает, что операции, которые блок могут вернуться преждевременно, а должны быть возобновлены, как упомянуто выше. Сигналы должны обрабатываться из очереди в основном потоке, а не рабочими пулами, так как это вновь вызывает проблему асинхронности. Однако управление очередью невозможно безопасным способом с использованием асинхронного сигнала с помощью только sig_atomic_t, поскольку только одиночные чтения и записи в такие переменные гарантированно будут атомарными, а не приращениями или (выборка-и) -декрементами, как если бы быть обязательным для очереди. Таким образом, фактически только один сигнал на обработчик может быть безопасно поставлен в очередь с sig_atomic_t до тех пор, пока он не будет обработан.
A Выполнение процесса может привести к генерации аппаратного исключения, например, если процесс пытается разделить на ноль или вызывает ошибка страницы.
В Unix-подобных операционных системах это событие автоматически изменяет контекст процессора , чтобы начать выполнение обработчика исключений kernel . В случае некоторых исключений, таких как сбой страницы, ядро имеет достаточно информации, чтобы полностью обработать само событие и возобновить выполнение процесса.
Другие исключения, однако, ядро не может обрабатывать интеллектуально, и вместо этого оно должно отложить операцию обработки исключения процессу, вызвавшему сбой. Эта отсрочка достигается с помощью механизма сигналов, при котором ядро отправляет процессу сигнал, соответствующий текущему исключению. Например, если процесс предпринял попытку целочисленного деления на ноль на x86 CPU, будет сгенерировано исключение ошибки разделения, и ядро отправит сигнал SIGFPE к процессу.
Аналогичным образом, если процесс попытался получить доступ к адресу памяти за пределами его виртуального адресного пространства, ядро уведомит процесс об этом нарушении с помощью сигнала SIGSEGV. Точное соответствие между именами сигналов и исключениями, очевидно, зависит от ЦП, поскольку типы исключений различаются в зависимости от архитектуры.
В приведенном ниже списке представлены сигналы, указанные в Single Unix Specification. Все сигналы определены как макроконстанты в заголовочном файле
. Имя макроконстанты состоит из префикса «SIG» , за которым следует мнемоническое имя сигнала.
abort()
стандартной библиотеки C, но он может быть отправлен процессу извне. как и любой другой сигнал.setitimer
) истекает. SIGALRM отправляется по истечении реального или часового времени. SIGVTALRM отправляется по истечении процессорного времени, используемого процессом. SIGPROF отправляется, когда процессорное время, используемое процессом и системой от имени процесса, истекает.wait
.killall -9
имеет аналогичный, но опасный эффект при выполнении, например. в Linux; он не позволяет программам сохранять несохраненные данные. У него есть другие опции, и без них используется более безопасный сигнал SIGTERM.Процесс может определять как обрабатывать входящие сигналы POSIX. Если процесс не определяет поведение для сигнала, то используется обработчик по умолчанию для этого сигнала. В таблице ниже перечислены некоторые действия по умолчанию для POSIX-совместимых систем UNIX, таких как FreeBSD, OpenBSD и Linux.
Signal | Portable. номер | Действие по умолчанию | Описание |
---|---|---|---|
SIGABRT | 6 | Завершить (дамп ядра) | Сигнал прерывания процесса |
SIGALRM | 14 | Завершить | Будильник |
SIGBUS | N/A | Завершить (дамп ядра) | Доступ к неопределенной части объекта памяти |
SIGCHLD | Н / Д | Игнорировать | Дочерний процесс завершен, остановлен или продолжен |
SIGCONT | N/A | Продолжить | Продолжить выполнение, если остановлено |
SIGFPE | 8 | Завершить (дамп ядра) | Ошибочная арифметическая операция |
SIGHUP | 1 | Завершить | Зависание |
SIGILL | 4 | Завершить (дамп ядра) | Недопустимая инструкция |
SIGINT | 2 | Завершить | Сигнал прерывания от терминала |
SIGKILL | 9 | Завершить | Убить (не может быть пойман или игнорируется) |
SIGPIPE | 13 | Terminate | Запись в канал, когда некому прочитать |
SIGPOLL | N/A | Terminate | Pollable event |
SIGPROF | N/A | Завершить | Таймер профилирования истек |
SIGQUIT | 3 | Завершить (дамп ядра) | Сигнал выхода из терминала |
SIGSEGV | 11 | Завершить (дамп ядра) | Неверная память ссылка |
SIGSTOP | N/A | Stop | Остановить выполнение (не может быть поймано или проигнорировано) |
SIGSYS | N / A | Завершить (дамп ядра) | Неверный системный вызов |
SIGTERM | 15 | Завершить | Сигнал завершения |
SIGTRAP | 5 | Завершить (дамп ядра) | Trap / прерывание точки останова |
SIGTSTP | N/A | Stop | Сигнал остановки терминала |
SIGTTIN | N / A | Остановить | Фоновый процесс пытается прочитать |
SIGTTOU | N/A | Stop | Фоновый процесс пытается записать |
SIGUSR1 | N/A | Terminate | Пользовательский сигнал 1 |
SIGUSR2 | N/A | Terminate | Пользовательский сигнал 2 |
SIGURG | N/A | Игнорировать | Out-of-ba nd данные доступны в сокете |
SIGVTALRM | N/A | Terminate | Срок действия виртуального таймера истек |
SIGXCPU | N / A | Завершить (дамп ядра) | Превышено ограничение по времени ЦП |
SIGXFSZ | N/A | Завершить (дамп ядра) | Превышен предел размера файла |
SIGWINCH | N/A | Игнорировать | Размер окна терминала изменен |
Следующие сигналы не указаны в спецификации POSIX. Однако иногда они используются в различных системах.