我的系统是 ubuntu 12.04。我从 修改了示例man 2 signalfd,并添加了示例sigaddset(&mask, SIGSEGV)。但是在SIGSEGV生成时我无法获得输出。
它是一个错误glibc吗?源代码片段如下:
sigemptyset(&mask);
sigaddset(&mask, SIGINT);
sigaddset(&mask, SIGQUIT);
sigaddset(&mask, SIGSEGV);
/* Block signals so that they aren't handled
according to their default dispositions */
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
handle_error("sigprocmask");
sfd = signalfd(-1, &mask, 0);
if (sfd == -1)
handle_error("signalfd");
int* a = NULL;
for (;;) {
s = read(sfd, &fdsi, sizeof(struct signalfd_siginfo));
if (s != sizeof(struct signalfd_siginfo))
handle_error("read");
if (fdsi.ssi_signo == SIGINT) {
printf("Got SIGINT\n");
(*a) = 1;
} else if (fdsi.ssi_signo == SIGQUIT) {
printf("Got SIGQUIT\n");
exit(EXIT_SUCCESS);
} else {
printf("Read unexpected signal\n");
}
}
Run Code Online (Sandbox Code Playgroud)
有关详细说明,请参阅这个和那个答案。仔细阅读signal(7)和signal-safety(7)。还要记住,您的进程的虚拟地址空间是该进程的所有线程共有的,并在它们之间共享。另请参阅proc(5)(并使用pmap(1))并尝试 从进程内部读取以了解其实际虚拟地址空间。/proc/self/maps
粗略地说,如果您SIGSEGV使用signalfd(2)处理(异步)(由内核在某些异常故障后产生),看起来您安装了一个“内核”信号处理程序,它神奇地“写入”某些文件上的一些字节描述符(您几乎可以signalfd通过在某些管道上安装信号处理程序来模仿;但signalfd保证一些“原子性”,否则您将不会拥有)。
当你从那个处理中回来时,机器处于相同的状态,所以 SIGSEGV 再次发生。
如果你想处理SIGSEGV你需要使用sigaction(2)或过时的signal(2)来安装处理例程(所以你不能signalfd用于 SIGSEGV),然后你应该要么
ucontext_t)提供给由sigactionwith安装的处理程序SA_SIGINFO),例如通过更改某些寄存器或更改地址空间(例如,通过从处理程序内部调用mmap(2))。洞察力是进入 SIGSEGV 处理程序,程序计数器设置为故障机器指令。当您从 SIGSEGV 处理程序返回时,寄存器处于提供给它的状态(指针ucontext_t作为sa_sigaction传递给的函数的第三个参数sigaction)。如果您不更改该状态,则会重新执行相同的机器指令,并且由于您没有更改任何内容,因此会发生相同的错误,并且内核会再次发送相同的 SIGSEGV 信号。
顺便说一句,软件巧妙且不可移植地处理 SIGSEGV 的一个很好的例子是Ravenbrook MPS垃圾收集库。它们的写屏障(用 GC 的说法)是通过处理 SIGSEGV 来实现的。这是非常聪明(且不可移植)的代码。
注意:在实践中,如果您只想显示回溯信息,您可以从SIGSEGV处理程序中完成(例如,通过使用GCC libbacktrace或backtrace(3)然后_exit(2) -ing 而不是从您的SIGSEGV信号处理程序返回);它并不完美,也不会总是有效——例如,如果你破坏了内存堆——因为你将调用非异步信号安全函数,但实际上工作得很好。最近的 GCC 正在这样做(在编译器内部,例如cc1plus及其插件),它有很大帮助。