我有这个学校项目,它是关于使用setjmp和longjmp进行不精确的计算。程序启动一个定时器,向信号处理程序发出信号。
在计时器到期之前,有一些迭代计算(出于演示目的,只是一个循环没有任何用处)。在这个循环的开头有一个setjmp调用,在信号处理程序中有一个longjmp调用。这基本上迫使循环停止计算中间并运行它调用longjmp的信号处理程序。
我遇到的问题是,每当计算部分非常短时,我似乎相当一致地断块,但是当计算部分很长(内部循环有很多次迭代)时,它运行得很好(没有遇到过还有段落错误。显然,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应该用在信号处理程序中,但这是我教授说的方式。另外,应该注意的是,在调用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
答案 0 :(得分:1)
您不能使用'longjmp'退出异步事件,如计时器。它仅用于保存和恢复由普通调用约定保存的寄存器和堆栈指针。
附注:请考虑在本地变量上使用volatile
关键字,如7.13.2.1中所述:
所有可访问的对象都有值, 和所有其他组成部分 抽象机器有状态,如
longjmp
函数的时间 叫,除了值 自动存储持续时间的对象 这是函数的本地 包含调用 没有的相应setjmp
宏 有易失性 - 限定类型并且有 在setjmp
之间进行了更改 调用和longjmp
调用 不确定的。
答案 1 :(得分:0)
您的信号处理程序将调用longjmp
,因此跳转目标最好有效。这意味着,先调用setjmp
,然后调用sigaction
和setitimer
。