setjmp和longjmp重复使用的问题

C本身没有异常机制。有时程序执行失败时,系统会发出信号进行处理。最常见的就是发生非法内存访问时报段错误,有时候我们并不希望这时程序就终止退出了,而希望发生这种情况时给出自己的处理过程让程序继续运行下去。当然找到访问错误的原因并改正更加重要,来个捕获处理却不失为一种救火的方法。

实现的一个思路是使用setjmp/longjmp组合,当捕捉到一个信号时,进入信号捕捉函数,处理后继续执行。然而使用这对组合后,此时当前信号被自动地加到进程的信号屏蔽字中,这阻止了后来产生的这种信号中断该信号处理程序。如果用longjmp跳出信号处理程序,信号屏蔽字不一定被恢复(各个发行版处理策略不同),从而再次执行到这里时我们自定义的处理过程失效。

举例

来个非法访问内存的程序。试图访问低地址的内存空间,os是不会允许的

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 
 4 #include <setjmp.h>
 5 #include <signal.h>
 6 jmp_buf Jump_Buffer;
 7 
 8 #define BLOCK "block"
 9 
10 static void sig_segv(int signo) {
11         signal(SIGSEGV, sig_segv);
12         if (signo == SIGSEGV){
13                 printf("sigsegvvvvvvvv\n");
14                 longjmp(Jump_Buffer, 1);
15         }
16 }
17 
18 int fun(){
19         int addr = 1;
20         char name[10];
21         if (signal(SIGSEGV, sig_segv) != sig_segv)
22                 signal(SIGSEGV, sig_segv);
23         if(setjmp(Jump_Buffer) != 0){//这里设置后longjmp将跳到这里
24                 printf("exceptions\n");
25                 strcpy(name, BLOCK);
26         }
27         else{
28                 strncpy(name, (char*)addr, 10);
29         }
30         name[9] = '\0';
31 
32         printf("%d,%d,%s\n", sizeof(BLOCK), strlen(BLOCK), name);
33         return 0;
34 }
35 
36 int main(){
37         fun();
38         fun();
39 
40         return 0;
41 }

预期我们希望执行strncpy出错时能跳到默认的处理过程中,达到C++异常的效果。然而,第二次调用fun()却仍然报段错误,如下

所幸的是,POSIX.1定义了另一对函数:sigsetjmp和siglongjmp。
sigsetjmp多了一个参数savemask,如果savemask非0,则sigsetjmp在env中保存进程的当前信号屏蔽字。调用siglongjmp时,如果带非0savemask的sigsetjmp调用已经保存了env,则siglongjmp从其中恢复保存的信号屏蔽字。这就解决了循环中调用setjmp,反复调用longjmp时,仅第一次longjmp生效的问题。

修改如下

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 
 4 #include <setjmp.h>
 5 #include <signal.h>
 6 jmp_buf Jump_Buffer;
 7 
 8 #define BLOCK "block"
 9 
10 static void sig_segv(int signo) {
11         signal(SIGSEGV, sig_segv);
12         if (signo == SIGSEGV){
13                 printf("sigsegvvvvvvvv\n");
14                 siglongjmp(Jump_Buffer, 1);//使用了siglongjmp
15         }
16 }
17 
18 int fun(){
19         int addr = 1;
20         char name[10];
21         if (signal(SIGSEGV, sig_segv) != sig_segv)
22                 signal(SIGSEGV, sig_segv);
23         if(sigsetjmp(Jump_Buffer, 1) != 0){//这里使用sigsetjmp,有第二个参数
24                 printf("exceptions\n");
25                 strcpy(name, BLOCK);
26         }
27         else{
28                 strncpy(name, (char*)addr, 10);
29         }
30         name[9] = '\0';
31 
32         printf("%d,%d,%s\n", sizeof(BLOCK), strlen(BLOCK), name);
33         return 0;
34 }
35 
36 int main(){
37         fun();
38         fun();
39 
40         return 0;
41 }

执行结果如下

达到了预期的目标。

posted on 2013-01-07 20:22  小交响曲  阅读(1161)  评论(0编辑  收藏  举报

导航