С обработать сигнал SIGFPE и продолжить выполнение

Я пытаюсь справиться с SIGFPE сигнал, но моя программа просто падает или работает вечно. Я должен использовать signal() а не другие, как sigaction(),

Итак, в моем коде у меня есть:

#include <stdio.h>
#include <signal.h>

void handler(int signum)
{
    // Do stuff here then return to execution below
}

int main()
{
    signal(SIGFPE, handler);

    int i, j;
    for(i = 0; i < 10; i++) 
    {
        // Call signal handler for SIGFPE
        j = i / 0;
    }

    printf("After for loop");

    return 0;
}

По сути, я хочу входить в обработчик каждый раз, когда происходит деление на 0. Он должен делать все, что ему нужно внутри handler() Затем функция продолжит следующую итерацию цикла.

Это также должно работать для других сигналов, которые должны быть обработаны. Любая помощь будет оценена.

2 ответа

Предостережение: извините за дождь на параде, но вы действительно не хотите этого делать.

Это совершенно справедливо для захвата [извне сгенерированных] сигналов, таких как SIGINT, SIGTERM, SIGHUP и т. д., чтобы обеспечить корректную очистку и завершение программы, в которой могут быть открыты файлы, которые частично записаны.

Тем не менее, внутренне генерируемые сигналы, такие как SIGILL, SIGBUS, SIGSEGV а также SIGFPE очень трудно оправиться от значимого. Первые три ошибки - чистые и простые. И, ИМО, SIGFPE это также серьезная ошибка.

После такого сигнала ваша программа находится в небезопасном и неопределенном состоянии. Даже ловить сигнал и делать longjmp/siglongjmp не исправить это.

И нет никакого способа точно сказать, насколько серьезен ущерб. Или насколько серьезным будет ущерб, если программа попытается продолжить работу.

Если вы получаете SIGFPE, это было для вычисления с плавающей запятой [которое вы могли бы сгладить]. Или это было для целочисленного деления на ноль? Какой расчет был сделан? И где? Вы не знаете

Попытка продолжить может иногда нанести 10-кратный ущерб, потому что теперь программа вышла из-под контроля. После восстановления программа может быть в порядке, но это не может быть. Таким образом, надежность программы после события, не может быть определена с какой-либо степенью уверенности.

Каковы были события (то есть) вычисления, которые привели к SIGFPE? Возможно, это не просто одно деление, а цепочка вычислений, которая привела к тому, что значение равно нулю. Куда делись эти ценности? Будут ли эти подозрительные значения использоваться кодом после выполнения операции восстановления?

Например, программа может перезаписать неправильный файл, потому что неудачный расчет каким-то образом был связан с выбором дескриптора файла, который будет использовать вызывающая сторона.

Или вы теряете память. Или испортить кучу. Или ошибка была в самом коде выделения кучи?

Рассмотрим следующую функцию:

void
myfunc(char *file)
{
    int fd;

    fd = open(file,O_WRONLY);

    while (1) {
        // do stuff ...

        // write to the file
        write(fd,buf,len);

        // do more stuff ...

        // generate SIGFPE ...
        x = y / z;
    }

    close(fd);
}

Даже с обработчиком сигнала, который делает siglongjmp, файл, который myfunc писал в сейчас поврежден / усечен. И дескриптор файла не будет закрыт.

Или что если myfunc читал из файла и сохранял данные в некоторый массив. Этот массив заполнен только частично. Теперь вы получаете SIGFPE, Это перехватывается обработчиком сигнала, который делает siglongjmp,

Один из абонентов myfunc делает sigsetjmp "поймать" это. Но что это может сделать? Звонящий не знает, как все плохо. Можно предположить, что буфер myfunc считывание полностью сформировано и запишите его в другой файл. Этот другой файл теперь поврежден.


ОБНОВИТЬ:

Ой, забыл упомянуть неопределенное поведение...

Обычно мы связываем UB, такой как запись за концом массива, с segfault [ SIGSEGV ]. Но что, если это вызывает SIGFPE вместо?

Это больше не просто "плохой расчет" - мы ловим [и игнорируем] UB на самой ранней точке обнаружения. Если мы сделаем восстановление, следующее использование может быть хуже.

Вот пример:

// assume these are ordered in memory as if they were part of the same struct:
int x[10];
int y;
int z;

void
myfunc(void)
{

    // initialize
    y = 23;
    z = 37;

    // do stuff ...

    // generate UB -- we run one past the end of x and zero out y
    for (int i = 0;  i <= 10;  ++i)
        x[i] = 0;

    // do more stuff ...

    // generate SIGFPE ...
    z /= y;

    // do stuff ...

    // do something _really_ bad with y that causes a segfault or _worse_
    // sends a space rocket off-course ...
}

Если вам нужно использовать сигнал для обработки FPE или любой другой сигнал, который вы вызываете напрямую, вызывая бессмысленность процессора, которая его вызывает, то определяется только то, что произойдет, если вы выйдете из программы из обработчика сигнала или воспользуетесь longjmp для выхода.

Также обратите внимание на точное размещение функций восстановления в конце ветви вычисления, но в начале ветви дескриптора.

К сожалению, вы не можете использовать signal() как это вообще; второй вызов приводит к падению кода. Вы должны использовать sigaction, если собираетесь обрабатывать сигнал более одного раза.

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
#include <string.h>

jmp_buf fpe;

void handler(int signum)
{
    // Do stuff here then return to execution below
    longjmp(fpe, 1);
}

int main()
{
    volatile int i, j;
    for(i = 0; i < 10; i++) 
    {
        // Call signal handler for SIGFPE
        struct sigaction act;
        struct sigaction oldact;
        memset(&act, 0, sizeof(act));
        act.sa_handler = handler;
        act.sa_flags = SA_NODEFER | SA_NOMASK;
        sigaction(SIGFPE, &act, &oldact);

        if (0 == setjmp(fpe))
        {
            j = i / 0;
            sigaction(SIGFPE, &oldact, &act);
        } else {
            sigaction(SIGFPE, &oldact, &act);
            /* handle SIGFPE */
        }
    }

    printf("After for loop");

    return 0;
}
Другие вопросы по тегам