局部变量有几种情况需要特殊说明:
请参考文章末尾的例子。
解决方法:加volatile, 或 对局部变量的操作放在setjmp与longjmp外。
#include
#include
int d = 4;
int e = 5;
int main()
{
int a = 1;
int b = 2;
int c = 3;
sigjmp_buf local_sigjmp_buf;
a = 10;
if (sigsetjmp(local_sigjmp_buf, 0) == 0)
{
b = 20;
d = 40;
siglongjmp(local_sigjmp_buf, 1);
}
else
{
c = 30;
e = 50;
}
printf("a = %d,b = %d,c = %d,d = %d, e = %d\n", a, b, c, d, e);
return 0;
}
使用O1编译
执行结果:b=20赋值丢失
$ gcc -o main3 -Wall -g -ggdb -O1 -g3 -gdwarf-2 main3.c
$ ./main3
a = 10,b = 2,c = 30,d = 40, e = 50
使用O0编译
执行结果:符合预期
$ gcc -o main3 -Wall -g -ggdb -O0 -g3 -gdwarf-2 main3.c
$ ./main3
a = 10,b = 20,c = 30,d = 40, e = 50
编译器在O1优化下,把sigsetjmp与siglongjmp之间的局部变量赋值操作丢掉了。
对比:左侧gcc O0,右侧gcc O1
手册中已有说明,满足三个条件的变量赋值无效:
LONGJMP(3) Linux Programmer's Manual LONGJMP(3)
NAME
longjmp, siglongjmp - nonlocal jump to a saved stack context
SYNOPSIS
#include
void longjmp(jmp_buf env, int val);
void siglongjmp(sigjmp_buf env, int val);
NOTES
The values of automatic variables are unspecified after a call to longjmp() if they meet all the following criteria:
· they are local to the function that made the corresponding setjmp(3) call;
· their values are changed between the calls to setjmp(3) and longjmp(); and
· they are not declared as volatile.
解法很简单:加volatile
这类一旦发生很难排查,事后排查难度远大于代码review发现。
Postgresql中的存在大量PG_TRY/PG_CATCH宏的使用:
例如
这类宏是对sigsetjmp、siglongjmp函数的一层封装:(这里给一段PG10的定义,比较简单)
// 全局变量
sigjmp_buf *PG_exception_stack = NULL;
// 宏定义
#define PG_TRY() \
do { \
sigjmp_buf *save_exception_stack = PG_exception_stack; \
ErrorContextCallback *save_context_stack = error_context_stack; \
sigjmp_buf local_sigjmp_buf; \
if (sigsetjmp(local_sigjmp_buf, 0) == 0) \
{ \
PG_exception_stack = &local_sigjmp_buf
#define PG_CATCH() \
} \
else \
{ \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack
#define PG_END_TRY() \
} \
PG_exception_stack = save_exception_stack; \
error_context_stack = save_context_stack; \
} while (0)
对于这几个宏,在使用时需要注意:
如果在PG_TRY里面修改了栈变量,一定要确认变量加了volatile,全局变量不受影响。
新版本的PG也在注释中做了提示。
结论:
#include
#include
#include
struct TEST
{
int d;
int e;
};
void func(int a, int *b)
{
sigjmp_buf local_sigjmp_buf;
struct TEST *p;
p = (struct TEST*) malloc(sizeof(struct TEST));
p->d = 4;
p->e = 5;
struct TEST *q;
q = (struct TEST*) malloc(sizeof(struct TEST));
q->d = 400000;
q->e = 500000;
int c = 3;
if (sigsetjmp(local_sigjmp_buf, 0) == 0)
{
a = 10;
*b = 20;
c = 30;
p->d = 40;
q = p;
siglongjmp(local_sigjmp_buf, 1);
}
else
{
printf("a = %d,b = %d, c = %d, p->d = %d, q->e = %d\n", a, *b, c, p->d, q->e);
}
}
int main()
{
int x = 2;
func(1, &x);
return 0;
}
// gcc -o main4 -Wall -g -ggdb -O0 -g3 -gdwarf-2 main4.c
结果
$ gcc -o main4 -Wall -g -ggdb -O0 -g3 -gdwarf-2 main4.c
$ ./main4
a = 10,b = 20, c = 30, p->d = 40, q->e = 5
$ gcc -o main4 -Wall -g -ggdb -O1 -g3 -gdwarf-2 main4.c
$ ./main4
a = 1,b = 20, c = 3, p->d = 40, q->e = 500000