【问题标题】:Segfaulting when using setjmp longjmp使用 setjmp longjmp 时出现段错误
【发布时间】:2011-03-30 17:45:00
【问题描述】:

我有这个学校项目,它是关于使用 setjmp 和 longjmp 进行不精确的计算。程序启动一个计时器,该计时器将向信号处理程序发出信号。

在计时器到期之前,有一些迭代计算(出于演示目的,只是一个无用的循环)。在这个循环的开始有一个 setjmp 调用,在信号处理程序中有一个 longjmp 调用。这基本上强制循环停止中间计算并运行它调用 longjmp 的信号处理程序。

我遇到的问题是,每当计算部分很短时,我似乎相当一致地段错误,但是当计算部分很长(内部循环有很多迭代)时,它运行得很好(没有遇到段错误)。显然,段错误必须发生在计算部分周围的区域,但我无法弄清楚它来自哪里,因为调试会像使用 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 应该在信号处理程序中使用,但这是我的教授所说的方式。另外,应该注意的是,在调用 longjmp 之后我会解除阻塞 SIGALRM(参见 main 的 else 语句)。

查看 dmesg 我得到:

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

]

【问题讨论】:

  • 如何使用调试器查找段错误在哪里?重要变量的值是什么?
  • 我正在使用使用 gdb 调试器的 netbeans。每当我在调试模式下运行时,它永远不会出现段错误,无法弄清楚原因。只有运行模式下的段错误。
  • 我认为您的大括号不匹配。请修正大括号和缩进。
  • newmaskoldmask 似乎已切换。在应该禁用信号时启用信号,或者看起来如此。

标签: c++ signals setjmp


【解决方案1】:

您不能使用“longjmp”退出异步事件,例如计时器。它仅用于保存和恢复正常调用约定保存的寄存器和堆栈指针。

旁注:考虑在局部变量上使用 volatile 关键字,如 7.13.2.1 中所述:

所有可访问的对象都有值, 和所有其他组件 抽象机有状态,截至 longjmp 函数的时间 称为,除了的值 自动存储期限的对象 函数本地的 包含调用 对应的setjmp 宏不 具有 volatile 限定类型并具有 在setjmp 之间进行了更改 调用和longjmp 调用是 不确定。

【讨论】:

    【解决方案2】:

    您的信号处理程序将调用longjmp,因此跳转目标最好是有效的。这意味着,首先调用setjmp,然后调用sigactionsetitimer

    【讨论】:

    • 同意,但是计时器设置为 1 秒,所以 setjmp 肯定会先运行 =p。我应该编辑它以遵循你所说的,谢谢。
    • @user623879:如果setjmp 在调用longjmp 时(再次)运行会发生什么?您需要防止这种情况发生,并且在设置信号之前调用setjmp(然后在信号未屏蔽时不调用它)是最可靠的方法。
    • 我试过你说的。代码在上面更新。基本上,我通过使用 setitimer 手动重置计时器来确保信号句柄永远不会中断 setjmp。我仍然得到段错误......虽然在调试模式下仍然没有段错误......令人困惑。我现在似乎有时会收到此错误:ld.so检测到不一致:dl-lookup.c:725:_dl_lookup_symbol_x:断言`version ==((void *)0)|| (flags & ~(DL_LOOKUP_ADD_DEPENDENCY | DL_LOOKUP_GSCOPE_LOCK)) == 0' 失败!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-10
    相关资源
    最近更新 更多