Выезд эта страница , которая имеет пример того, как сделать это.
На странице руководства для longjmp:
POSIX не указывает, следует ли
longjmp ()
восстановит сигнал контекст. Если вы хотите сэкономить и восстановите маски сигналов, используйтеsiglongjmp ()
Ваш второй вопрос: Да, функция вернет -2, потому что longjmp ()
заставит его перейти в setjmp (буфер)
часть, но время должно быть очень точным.
Когда дело доходит до того, что может пойти не так, когда поведение не определено, единственный ответ, на который вы можете рассчитывать, - «все, в том числе ничего». Может быть, что-то пойдет не так, возможно, вы получите segfault, возможно, у вас будут носовые демоны.
Более конкретные ответы зависят от того, с какой системой и выпуском вы работаете. Например, в дистрибутивах Linux (по крайней мере, все с 2000 года) ядро выполняет некоторые задачи после возврата обработчика сигналов. Если вы используете longjmp, вы, вероятно, оставите в стеке ядра мусор, который может вызвать проблемы в дальнейшем, например, ошибочный возврат к коду, который ваша программа выполняла, когда сигнал был пойман (вызов 'fgets' в примере). Или нет.
Вызов longjmp в обработчике сигнала также может (в общем,
Я не думаю, что вам нужно использовать setjmp
/ longjmp
. fgets
должен прерываться сигналами (для errno задано значение EINTR), хотя вам, вероятно, потребуется использовать sigaction (...)
вместо signal (... )
, чтобы убедиться, что SA_RESTART очищен.
void timeout(int) {
// doesn't actually need to do anything
}
int t_gets(char* s, int t)
{
char* ret;
struct sigaction action = {0};
action.sa_handler = timeout;
sigaction(SIGALRM, &action, NULL);
alarm(t);
// if fgets() does not return in t seconds, SIGALARM handler timeout()
// will be called, interrupting fgets and causing t_gets() to return -2
ret = fgets(s, 100, stdin);
// even if the alarm is called after fgets returns, it won't erroneously cause
// t_gets to return -2
int err = errno;
alarm(0);
if (ret == NULL) {
switch (err) {
case EINTR:
return -2;
// add other cases as warranted
default:
return -1;
}
}
return strlen(s);
}
По поводу второго вопроса: вы можете добавить блокировку, которая блокирует возврат -2, когда основной поток прошел вызов fgets.
что делать, если тревога срабатывает сразу после fgets () возвращает, но до тревоги (0) вызывается?
Вы можете инициализировать ret
(возможно, равным NULL) и проверить это в теле оператора if (setjmp ())
:
/* NOT TESTED */
int t_gets(char* s, int t)
{
char* ret = NULL;
signal(SIGALRM, timeout);
if (setjmp(buffer) != 0) {
// timeout() will jump here
if (ret == NULL) {
return -2;
} else {
goto end_of_function;
}
}
alarm(t);
// if fgets() does not return in t seconds, SIGALARM handler timeout()
// will be called, causing t_gets() to return -2
ret = fgets(s, 100, stdin);
end_of_function:
alarm(0);
if (ret == NULL ) return -1;
return strlen(s);
}
Вы можете заменить longjmp / setjmp на siglongjmp / sigsetjump, и тогда не будет проблемы с неопределенным контекстом сигнала после jmp. Возможно, вам все равно, поскольку вы явно не меняете маску. Я забываю, изменяется ли маска самим сигнальным вызовом.
Возможно, более серьезной проблемой является обеспечение безопасности вашего кода для сигналов. Например, получает ли fgets () какой-либо мьютекс (возможно, неявно как часть вызова malloc)? Если это так, и ваш таймер отключится, пока этот мьютекс удерживается, ваша программа будет всплывать в следующий раз, когда вы попытаетесь выделить кучу.