C Language
Обработка сигналов
Поиск…
Синтаксис
- void (* signal (int sig, void (* func) (int))) (int);
параметры
параметр | подробности |
---|---|
сиг | Сигнал для установки обработчика сигналов, один из SIGABRT , SIGFPE , SIGILL , SIGTERM , SIGINT , SIGSEGV или некоторое определенное значение реализации |
FUNC | Обработчик сигнала, который является одним из следующих: SIG_DFL , для обработчика по умолчанию, SIG_IGN для игнорирования сигнала или указателя функции с сигнатурой void foo(int sig); , |
замечания
Использование обработчиков сигналов только с гарантиями из стандарта C накладывает различные ограничения, которые могут или не могут быть выполнены в пользовательском обработчике сигналов.
Если функция, определенная пользователем, возвращается при обработке
SIGSEGV
,SIGFPE
,SIGILL
или любого другого аппаратного прерывания, определяемого реализацией, поведение не определено стандартом C. Это связано с тем, что интерфейс C не дает средств для изменения неисправного состояния (например, после деления на0
), и поэтому при возврате программа находится в точно таком же ошибочном состоянии, что и до того, как произошло прерывание аппаратного обеспечения.Если пользовательская функция была вызвана в результате вызова
abort
илиraise
, обработчик сигнала не может снова вызватьraise
.Сигналы могут поступать в середине любой операции, и поэтому неделимость операций может вообще не гарантироваться, и обработка сигналов не работает с оптимизацией. Поэтому все изменения данных в обработчике сигналов должны быть в переменных
- типа
sig_atomic_t
(все версии) или атомарного типа без блокировки (поскольку C11, необязательный) - которые являются
volatile
.
- типа
Другие функции из стандартной библиотеки C обычно не будут соблюдать эти ограничения, поскольку они могут изменять переменные в глобальном состоянии программы. Стандарт С только делает гарантии для
abort
,_Exit
(с С99),quick_exit
(с С11),signal
(для того же самого числа сигналов), а также некоторые атомарные операции (начиная с С11).
Поведение не определено стандартом C, если нарушено какое-либо из вышеперечисленных правил. Платформы могут иметь определенные расширения, но они, как правило, не переносятся за пределы этой платформы.
Обычно системы имеют свой собственный список функций, которые безопасны для асинхронного сигнала , то есть из функций библиотеки C, которые могут использоваться из обработчика сигналов. Например, часто функция
printf
входит в число этих функций.В частности, стандарт C не определяет многое о взаимодействии с его интерфейсом потоков (начиная с C11) или с любыми конкретными библиотеками потоков, такими как потоки POSIX. Такие платформы должны сами определять взаимодействие таких библиотек потоков с сигналами.
Обработка сигналов с помощью «signal ()»
Номера сигналов могут быть синхронными (например, SIGSEGV
- segmentation fault), когда они срабатывают при неправильной работе самой программы или асинхронной (например, SIGINT
- интерактивное внимание), когда они инициируются извне программы, например, Cntrl-C
клавиши Cntrl-C
.
Функция signal()
является частью стандарта ISO C и может использоваться для назначения функции для обработки определенного сигнала
#include <stdio.h> /* printf() */
#include <stdlib.h> /* abort() */
#include <signal.h> /* signal() */
void handler_nonportable(int sig)
{
/* undefined behavior, maybe fine on specific platform */
printf("Catched: %d\n", sig);
/* abort is safe to call */
abort();
}
sig_atomic_t volatile finished = 0;
void handler(int sig)
{
switch (sig) {
/* hardware interrupts should not return */
case SIGSEGV:
case SIGFPE:
case SIGILL:
/* quick_exit is safe to call */
quick_exit(EXIT_FAILURE);
/* use _Exit in pre-C11 */
_Exit(EXIT_FAILURE);
default:
/* Reset the signal to the default handler,
so we will not be called again if things go
wrong on return. */
signal(sig, SIG_DFL);
/* let everybody know that we are finished */
finished = sig;
return;
}
}
int main(void)
{
/* Catch the SIGSEGV signal, raised on segmentation faults (i.e NULL ptr access */
if (signal(SIGSEGV, &handler) == SIG_ERR) {
perror("could not establish handler for SIGSEGV");
return EXIT_FAILURE;
}
/* Catch the SIGTERM signal, termination request */
if (signal(SIGTERM, &handler) == SIG_ERR) {
perror("could not establish handler for SIGTERM");
return EXIT_FAILURE;
}
/* Ignore the SIGINT signal, by setting the handler to `SIG_IGN`. */
signal(SIGINT, SIG_IGN);
/* Do something that takes some time here, and leaves
the time to terminate the program from the keyboard. */
/* Then: */
if (finished) {
fprintf(stderr, "we have been terminated by signal %d\n", (int)finished);
return EXIT_FAILURE;
}
/* Try to force a segmentation fault, and raise a SIGSEGV */
{
char* ptr = 0;
*ptr = 0;
}
/* This should never be executed */
return EXIT_SUCCESS;
}
Использование signal()
налагает важные ограничения на то, что вам разрешено делать внутри обработчиков сигналов, см. Примечания для получения дополнительной информации.
POSIX рекомендует использовать sigaction()
вместо signal()
из-за его неопределенного поведения и значительных вариантов реализации. POSIX также определяет гораздо больше сигналов, чем стандарт ISO C, включая SIGUSR1
и SIGUSR2
, которые могут использоваться свободно программистом для любых целей.