Ошибка при использовании setjmp longjmp - PullRequest
1 голос
/ 30 марта 2011

У меня есть этот школьный проект, и он использует setjmp и longjmp для неточных расчетов.Программа запускает таймер, который будет сигнализировать обработчику сигнала.

Перед истечением таймера, есть некоторые итерационные вычисления (для демонстрационных целей, просто цикл, который не делает ничего полезного).В начале этого цикла происходит вызов setjmp, а в обработчике сигнала - вызов longjmp.Это в основном заставляет цикл останавливать вычисление в середине и запускать обработчик сигнала там, где он вызывает longjmp.

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

Вот код, который у меня есть:

#include <iostream>
#include <signal.h>
#include <sys/time.h>
#include <setjmp.h>
#include <errno.h>
#include <stdlib.h>

jmp_buf env;


static void usr_timer_tick(int signal)
{
    if(signal == SIGALRM)
    {
        longjmp(env, 1);
    }
}


/*Program Description
 * This program first sets up the timer to signal usr_timer_tick
 * every 1 second on the SIGALRM signal. It then proceeds to do an iterated calculation three times.
 * An infinite loop calls setjmp and when 0 is returned, continues doing
 * a calculation on temp. After an iteration is complete, the result of
 * the iteration is saved into finalResult after blocking SIGALRM to
 * make the saving of the result atomic.
 *
 * Once the signal handler(usr_timer_tick) is triggered, it calls longjmp which forces
 * setjmp to return a non-zero value, which causes the main function to break out
 * of the infinite loop and start a new calculation...this is done a total of 3
 * times for demonstration purposes.
 */
int main(int argc, char **argv)
{

    //init timer using setitimer..real mode
    int which = ITIMER_REAL;
    struct itimerval value;
    struct sigaction sact;
    sigset_t newmask, oldmask;
    sigemptyset( &newmask );
    sigemptyset( &oldmask );
    sigaddset(&newmask, SIGALRM);

    sact.sa_flags = 0;
    sact.sa_handler = usr_timer_tick;
    sigaction( SIGALRM, &sact, NULL );
//    value.it_interval.tv_sec = 0;        /* One second */
//    value.it_interval.tv_usec = 0;
//    value.it_value.tv_sec = 1;           /* One second */
//    value.it_value.tv_usec = 0;
//
//    setitimer(which, &value, NULL);



    double finalResult = 0;
    int loopcount = 0;
    double tempResult = 0;
    for(int j = 0; j < 10; j++)
    {
        loopcount = 0;


            std::cout << "Run " << j << " begin loop "
            << loopcount << "\n";


            if(setjmp(env) == 0)
            {   //timer not hit yet

                //sigprocmask(SIG_BLOCK, &newmask, NULL);
                value.it_interval.tv_sec = 0;        /* One second */
                value.it_interval.tv_usec = 0;
                value.it_value.tv_sec = 1;           /* One second */
                value.it_value.tv_usec = 0;

                setitimer(which, &value, NULL);

                //sigprocmask(SIG_SETMASK, &oldmask, NULL);
                for(;;)
                {
                    //Do some random calculations
                    for(int i = 0; i < 1; i++)
                    {
                        tempResult = tempResult + .001;
                    }

                    //block signal from arriving and save to finalResult
                    if(sigprocmask(SIG_BLOCK, &newmask, NULL) < 0) exit(-1);
                    finalResult = tempResult;
                    std::cout << "Run " << j << " complete loop "
                        << loopcount << " result = " << finalResult<< "\n";
                    loopcount++;
                    if(sigprocmask(SIG_SETMASK, &oldmask, NULL)< 0) exit(errno);
                }
            }
            else
            {
                //timer signal arrived, print the final result and get out of loop
                std::cout << "***Run " << j << " killed on loop "
                        << loopcount << " result = "<< finalResult << "\n";
                sigprocmask(SIG_SETMASK, &oldmask, NULL);
                //break;
            }



    }
    return 0;
}

Я понимаю, что некоторые из вас, возможно, не согласятся с тем, что longjmp следует использовать в обработчике сигналов, но именно так сказал мой профессор.Также следует заметить, что я разблокирую SIGALRM после вызова longjmp (см. Еще оператор main).

Глядя на dmesg, я получаю:

 [121395.233842] cppapplication_[17397]: 
segfault at 2 ip b74656f6 sp bfbb5abc 
error 6 in libc-2.12.1.so[b743b000+157000

]

Ответы [ 2 ]

1 голос
/ 30 марта 2011

Вы не можете использовать «longjmp» для выхода из асинхронного события, такого как таймер.Он предназначен только для сохранения и восстановления регистров и указателя стека, которые сохраняются в соответствии с обычным соглашением о вызовах.

Примечание: рассмотрите возможность использования ключевого слова volatile для локальных переменных, как указано в 7.13.2.1.:

Все доступные объекты имеют значения, а все остальные компоненты абстрактной машины имеют состояние на момент вызова функции longjmp, за исключением того, что значения объектов длительности автоматического хранения, которыеявляются локальными по отношению к функции, содержащей вызов соответствующего макроса setjmp, который не имеет volatile -качественного типа и был изменен между вызовами setjmp и longjmp, являются неопределенными.

0 голосов
/ 30 марта 2011

Ваш обработчик сигнала будет вызывать longjmp, поэтому цель перехода должна быть действительной. Это значит, сначала позвоните setjmp, затем sigaction и setitimer.

...