我的系统是ubuntu 12.04。我从修改了示例man 2 signalfd,并添加了示例sigaddset(&mask, SIGSEGV)。但是SIGSEGV生成时我无法获得输出。
man 2 signalfd
sigaddset(&mask, SIGSEGV)
SIGSEGV
是一个错误glibc吗?源代码的片段如下:
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"); } }
请参阅此答案以及该答案以获取详细说明。仔细阅读signal(7)和signal-safety(7)。也请记住,虚拟地址空间的的过程中是常见的,和之间,所有的共享线程是进程。另请参见proc(5)(并使用pmap(1)),并尝试/proc/self/maps 从您的进程内部进行读取以了解其实际的虚拟地址空间。
/proc/self/maps
概括地说,如果您SIGSEGV使用signalfd(2)处理(异步)(在发生异常错误后由内核生成),则好像您安装了“内核”信号处理程序,该信号处理程序神奇地“写入”了某些文件中的某些字节描述符(signalfd通过在某个管道上安装信号处理程序,您几乎可以模仿一下;但是可以signalfd保证您不会有其他的“原子性”)。
signalfd
当您从该处理中返回时,机器处于相同状态,因此SIGSEGV再次发生。
如果要处理SIGSEGV,则需要使用sigaction(2)或过时的方法signal(2)来安装处理例程(因此不能signalfd用于SIGSEGV),然后应该
signal(2)
ucontext_t
sigaction
SA_SIGINFO
洞察力是输入了SIGSEGV处理程序,并将程序计数器设置为故障机器指令。当您从SIGSEGV处理程序返回时,寄存器处于给定的状态(指针ucontext_t作为sa_sigaction传递给的函数的第三个参数sigaction)。如果不更改该状态,则将重新执行相同的机器指令,并且由于未更改任何内容,因此会发生相同的错误,并且内核会再次发送相同的SIGSEGV信号。
sa_sigaction
顺便说一句,Ravenbrook MPS垃圾收集库是一个巧妙且非便携地处理SIGSEGV的软件的很好示例。它们的写屏障(按照GC的说法)是通过处理SIGSEGV来实现的。这是非常聪明的(非便携式)代码。
注意:实际上,如果您只想显示回溯信息,则可以从SIGSEGV处理程序中进行处理(例如,通过使用GCC libbacktrace或backtrace(3)然后_exit(2) -ing而不是从SIGSEGV信号处理程序中返回);它不是完美的,并且将永远无法工作(例如,如果您破坏了内存堆),因为您将调用非异步信号安全函数,但实际上效果很好。最近的GCC正在这样做(在编译器(例如cc1plus,及其插件)内部),并且对您有很大帮助。
cc1plus