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 }
执行结果如下
达到了预期的目标。