Выполнение обработчика сигнала по умолчанию
Я написал приложение, в котором я зарегистрировал номер обработчика сигналов для разных сигналов в Linux.
После того как процесс получает сигнал, управление передается обработчику сигнала, который я зарегистрировал. В этом обработчике я выполняю некоторую работу, которую мне нужно сделать, а затем я хотел бы вызвать обработчик сигнала по умолчанию i.e SIF_DFL
или SIG_IGN
.
Однако SIG_DFL
и SIG_ING
являются макросами, которые расширяются до числовых значений 0 и 1 соответственно, что является недопустимыми адресами функций.
Есть ли способ, которым я могу вызывать действия по умолчанию i.e SIG_DFL
или SIG_IGN
?
Чтобы достичь эффекта SIG_DFL
или SIG_ING
, я вызываю exit (1) и ничего не делаю, соответственно. Но для сигналов типа SIGSEGV
я также хотел бы иметь базовый дамп.
В общем, я бы хотел, чтобы мое поведение по умолчанию было таким же, как SIG_DFL
, и игнорировало поведение, подобное SIG_IGN
, как будет работать операционная система.
Ответы
Ответ 1
Справочное руководство по библиотеке GNU C содержит целую главу, объясняющую все, что касается обработки сигналов.
Когда вы устанавливаете свой собственный обработчик, вы всегда получаете обработчик ранее заданного сигнала (указатель функции) (см. manpages для signal()
или sigaction()
).
previous_handler = signal(SIGINT, myhandler);
Общее правило состоит в том, что вы всегда можете reset передать предыдущему обработчику и raise()
сигнал.
void myhandler(int sig) {
/* own stuff .. */
signal(sig, previous_handler);
raise(sig);
/* when it returns here .. set our signal handler again */
signal(sig, myhandler);
}
Существует один недостаток общего правила: аппаратные исключения, которые сопоставляются с сигналами, обычно назначаются определенной команде, которая вызвала исключение. Итак, когда вы снова поднимаете сигнал, связанная с ним инструкция не такая, как первоначально. Это может не повредить другие обработчики сигналов.
Другим недостатком является то, что каждый поднятый сигнал вызывает много времени обработки. Чтобы предотвратить чрезмерное использование raise()
, вы можете использовать следующие альтернативы:
-
В случае SIG_DFL
указатель на функцию указывает на адрес 0
(который, очевидно, не имеет действительного адреса). Таким образом, вы должны иметь reset обработчик и raise()
сигнал снова.
if (previous_handler == SIG_DFL)
{
signal(sig, SIG_DFL);
raise(sig);
signal(sig, myhandler);
}
-
SIG_IGN
имеет значение 1
(также неверный адрес). Здесь вы можете просто вернуться (ничего не делать).
else if (previous_handler == SIG_IGN)
{
return;
}
-
В противном случае (ни SIG_IGN
, ни SIG_DFL
) вы не получили действительного указателя функции, а может вызвать обработчик напрямую,
else
{
previous_handler(sig);
}
Конечно, вы должны также рассмотреть различные API-интерфейсы (см. manpages для signal()
и sigaction()
).
Ответ 2
Вы можете сохранить предыдущий обработчик, а затем вызвать его, когда придет время.
Установить обработчик. Убедитесь, что вы сохранили старый обработчик
static struct sigaction new_sa, old_sa;
new_sa.sa_handler = my_handler;
sigemptyset(&new_handler.sa_mask);
if (sigaction(signo, &new_sa, &old_sa) == -1) {
/* handle sigaction error */
}
В вашем новом обработчике вызовите старый обработчик
(*old_sa.sa_handler)(signo)
Вам не нужно поднимать это снова или делать что-то грязное; просто вызовите старый обработчик (конечно, поскольку вы сохранили sigaction
, у вас есть доступ к старому расположению и т.д.).
Ответ 3
Обычный подход к reset обработчику сигнала, а затем raise()
сигнал снова:
Здесь пример обработчика SIGINT:
void sigint_handler(int num)
{
/* handle SIGINT */
// call default handler
signal(SIGINT, SIG_DFL);
raise(SIGINT);
}
Ответ 4
Учитывая, что обработчики сигналов реализованы в ядре, единственный способ, который я вижу, -
- reset обработчик и
-
raise()
снова сигнал