段错误(SEGV)异常是否仅能触发一次?附测试验证
段错误(SEGV)异常是否仅能触发一次?
答案是:不是,但你的测试代码里因为信号处理的默认行为限制,导致第二次触发SEGV时直接终止进程,只捕获到一次。下面结合你的测试代码和现象逐一分析:
测试现象拆解
你的测试逻辑是:通过setjmp保存执行环境,触发异常后用longjmp跳回异常触发前的位置,尝试重复触发信号。结果是:
- SEGV信号仅被捕获一次,之后进程直接终止;
- 取消注释
assert(1 == 3)后,ABRT信号会被无限捕获,进入循环。
核心原因分析
1. SEGV仅触发一次的原因
你使用的signal()函数在多数Linux系统默认的System V风格实现中,当信号处理函数被调用前,系统会自动将该信号的处理方式重置为默认(SIG_DFL)。
对应到你的代码:
- 第一次触发SEGV(
*(int *)0)时,进入自定义的handle_segment_violation函数,通过longjmp跳回主逻辑,此时i被修改为1; - 再次执行
*(int *)1触发SEGV时,该信号的处理方式已经被重置为默认,系统直接执行默认操作——终止进程,因此不会再进入你的处理函数,只看到一次catch segv输出。
如果想要多次捕获SEGV,需要在信号处理函数中重新注册信号处理逻辑,修改后的处理函数如下:
void handle_segment_violation(int sig) { printf("catch segv\n"); signal(SIGSEGV, handle_segment_violation); // 重新注册信号处理 longjmp(g_position_env, 2); }
2. ABRT无限循环的原因
assert(1 == 3)会调用abort()函数,abort()的特殊行为导致了无限循环:
abort()会向进程发送SIGABRT信号;- 如果你的信号处理函数通过
longjmp直接跳走(而非正常返回),abort()的后续终止逻辑不会执行; - 程序跳回
loop_abrt标签后,再次执行assert,再次触发abort()发送SIGABRT,从而形成循环。
另外,abort()本身的逻辑规定:如果信号处理函数正常返回,它会重置SIGABRT的处理方式为默认并再次发送信号,确保进程终止。但你的代码中用longjmp跳过了这个流程,所以能持续触发信号。
完整测试代码(修正SEGV多次捕获)
#include <stdio.h> #include <assert.h> #include <signal.h> #include <setjmp.h> jmp_buf g_position_env; void handle_abort(int sig) { printf("catch abort\n"); longjmp(g_position_env, 1); } void handle_segment_violation(int sig) { printf("catch segv\n"); signal(SIGSEGV, handle_segment_violation); // 重新注册信号处理 longjmp(g_position_env, 2); } int main() { unsigned int i; int ret; signal(SIGABRT, handle_abort); signal(SIGSEGV, handle_segment_violation); i = 0; ret = setjmp(g_position_env); if (ret == 1) { goto loop_abrt; } else if (ret == 2) { i++; goto loop_segv; } loop_abrt: // uncomment code below, it will enter a loop //assert(1 == 3); loop_segv: printf("i = %d\n", i); printf("%d\n", *(int *)i); printf("-----------end\n"); return 0; }
内容的提问来源于stack exchange,提问作者Divlaker
相关产品推荐
相关产品推荐

