为什么我的信号处理程序在这里不被多次调用?
Why is my signal handler not invoked more than once here?
jmp_buf functjmp;
void sigsegv_handler(int sig) {
sio_printf("Caught sigsegv!\n");
siglongjmp(functjmp, 2);
return;
}
void foo(unsigned val) {
assert(0);
sio_printf("entered!\n");
}
int main() {
struct sigaction action;
action.sa_handler = sigsegv_handler;
sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
sigaddset(&action.sa_mask, SIGSEGV);
action.sa_flags = SA_RESTART; /* Restart syscalls if possible */
if (sigaction(SIGSEGV, &action, NULL) < 0) {
sio_fprintf(stderr, "handler error!\n");
}
sigset_t prev_mask;
sigprocmask(SIG_BLOCK, NULL, &prev_mask);
if (sigsetjmp(functjmp, 0) == 0) {
foo(*(unsigned *)0x8);
} {
sigprocmask(SIG_BLOCK, &prev_mask, NULL);
sio_printf("jump handled!\n");
foo(*(unsigned *)0x8);
}
sio_fprintf(stderr, "how did it come here?!\n");
}
我一直在使用 gdb 调试这段代码,我无法弄清楚为什么程序不使用我自己的处理程序处理第二个 SIGSEGV 信号,假设程序没有接收或发送其他信号?任何带有 sio 前缀的函数都是 stdio 对应项的异步安全变体。
目前,我猜测它与我在关于从信号处理程序返回的概念中遗漏的东西有关,而 longjmp 根本没有这样做。
简短回答:对于 C 程序,通常无法在 SIGSEGV 之后恢复。您可能会使用 C++ 获得更多里程。
长答案:参见 Coming back to life after Segmentation Violation
中的讨论
假设可以承担未定义行为的风险:
可以重新启用 SEGV。核心问题是在信号处理程序期间,代码明确阻止 SEGV 信号被触发(使用 sigaddset)。此外,(信号处理程序的)默认行为是在信号处理期间,相同的信号处理将推迟到信号处理程序returns。在 OP 代码中,信号处理程序从不 returns (因为 siglongjmp)
这两个问题都可以通过更改原始代码来解决。
// Make sure all attributes are NULL.
struct sigaction action = {} ;
action.sa_handler = sigsegv_handler;
sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
// Not Needed:: sigaddset(&action.sa_mask, SIGSEGV);
// Add SA_NODEFER to disable the deferred processing of SIGSEGV.
action.sa_flags = SA_RESTART | SA_NODEFER ; /* Restart syscalls if possible */
// rest of code here
if (sigaction(SIGSEGV, &action, NULL) < 0) {
sio_fprintf(stderr, "handler error!\n");
}
...
jmp_buf functjmp;
void sigsegv_handler(int sig) {
sio_printf("Caught sigsegv!\n");
siglongjmp(functjmp, 2);
return;
}
void foo(unsigned val) {
assert(0);
sio_printf("entered!\n");
}
int main() {
struct sigaction action;
action.sa_handler = sigsegv_handler;
sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
sigaddset(&action.sa_mask, SIGSEGV);
action.sa_flags = SA_RESTART; /* Restart syscalls if possible */
if (sigaction(SIGSEGV, &action, NULL) < 0) {
sio_fprintf(stderr, "handler error!\n");
}
sigset_t prev_mask;
sigprocmask(SIG_BLOCK, NULL, &prev_mask);
if (sigsetjmp(functjmp, 0) == 0) {
foo(*(unsigned *)0x8);
} {
sigprocmask(SIG_BLOCK, &prev_mask, NULL);
sio_printf("jump handled!\n");
foo(*(unsigned *)0x8);
}
sio_fprintf(stderr, "how did it come here?!\n");
}
我一直在使用 gdb 调试这段代码,我无法弄清楚为什么程序不使用我自己的处理程序处理第二个 SIGSEGV 信号,假设程序没有接收或发送其他信号?任何带有 sio 前缀的函数都是 stdio 对应项的异步安全变体。 目前,我猜测它与我在关于从信号处理程序返回的概念中遗漏的东西有关,而 longjmp 根本没有这样做。
简短回答:对于 C 程序,通常无法在 SIGSEGV 之后恢复。您可能会使用 C++ 获得更多里程。
长答案:参见 Coming back to life after Segmentation Violation
中的讨论假设可以承担未定义行为的风险:
可以重新启用 SEGV。核心问题是在信号处理程序期间,代码明确阻止 SEGV 信号被触发(使用 sigaddset)。此外,(信号处理程序的)默认行为是在信号处理期间,相同的信号处理将推迟到信号处理程序returns。在 OP 代码中,信号处理程序从不 returns (因为 siglongjmp)
这两个问题都可以通过更改原始代码来解决。
// Make sure all attributes are NULL.
struct sigaction action = {} ;
action.sa_handler = sigsegv_handler;
sigemptyset(&action.sa_mask); /* Block sigs of type being handled */
// Not Needed:: sigaddset(&action.sa_mask, SIGSEGV);
// Add SA_NODEFER to disable the deferred processing of SIGSEGV.
action.sa_flags = SA_RESTART | SA_NODEFER ; /* Restart syscalls if possible */
// rest of code here
if (sigaction(SIGSEGV, &action, NULL) < 0) {
sio_fprintf(stderr, "handler error!\n");
}
...