为什么不能使用signalfd捕获SIGSEGV? [英] Why can't capture SIGSEGV using signalfd?
问题描述
我的系统是ubuntu 12.04.
我从man 2 signalfd
修改示例,并在示例中添加sigaddset(&mask, SIGSEGV)
.但是生成SIGSEGV
时我无法获得输出.
My system is ubuntu 12.04.
I modify the example from man 2 signalfd
, and add sigaddset(&mask, SIGSEGV)
in the exmaple. But I can't get the output when SIGSEGV
is generated.
是glibc
的错误吗?源代码的片段如下:
Is it a bug of glibc
? The fragment of source code is following:
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)和虚拟地址空间. wikipedia.org/wiki/Process_(计算)"rel =" nofollow noreferrer>过程是所有 proc(5)(并使用 pmap(1)),然后尝试从过程内部读取/proc/self/maps
了解其实际的虚拟地址空间.
See this and that answers for detailed explanations. Read carefully signal(7) and signal-safety(7). Remember also that the virtual address space of your process is common to, and shared between, all the threads of that process. See also proc(5) (and use pmap(1)) and try reading /proc/self/maps
from inside your process to understand its actual virtual address space.
总的来说,如果您使用SIGSEGV(由内核在某些异常错误后生成). 2.html"rel =" nofollow noreferrer> signalfd(2),看来您安装了内核"信号处理程序,该信号处理程序神奇地写入"了一些文件描述符上的一些字节(您几乎可以模仿一下) signalfd
通过在某个管道上安装写一个信号处理程序来实现;但是signalfd
保证了某些您不会具有的原子性".
Grossly speaking, if you handle (an asynchronous) SIGSEGV
(produced by the kernel after some exception fault) with signalfd(2), it is looking like you installed a "kernel" signal handler which magically "write"-s some bytes on some file descriptor (you almost could mimick signalfd
by installing a signal handler writing on some pipe; but signalfd
guarantees some "atomicity" that you won't have otherwise).
当您从该处理中恢复时,机器处于相同状态,因此SIGSEGV再次发生.
When you are back from that handling, the machine is in the same condition, so the SIGSEGV happens again.
如果要处理SIGSEGV
,则需要使用 sigaction (2)或已过时的signal(2)
安装处理例程(因此,对于SIGSEGV,您不能使用signalfd
),那么您应该
If you want to handle SIGSEGV
you need to use sigaction(2) or the obsolete signal(2)
to install a handling routine (so you can't use signalfd
for SIGSEGV), and then you should either
- (或多或少可移植)避免从信号处理程序返回(例如,通过调用 sigaction(2))
- (以处理器和操作系统特定的方式)非便携式地更改计算机上下文(由第三个参数(指向某些处理器特定的
ucontext_t
的指针)赋予由sigaction
和SA_SIGINFO
安装的处理程序) ,例如通过更改某些寄存器,或更改地址空间(例如,通过调用 mmap( 2)(从处理程序内部).
- (more or less portably) avoid returning from your signal handler (e.g. by calling siglongjmp(3) from your signal handler installed with sigaction(2))
- non-portably (in a processor and operating system specific way) change the machine context (given by the third argument (a pointer to some processor specific
ucontext_t
) to your handler installed bysigaction
withSA_SIGINFO
), e.g. by changing some registers, or change the address space (e.g. by calling mmap(2) from inside the handler).
洞察力是输入SIGSEGV处理程序,并将程序计数器设置为故障机器指令.从SIGSEGV处理程序返回时,寄存器处于给定的状态(指针ucontext_t
作为传递给sigaction
的sa_sigaction
函数的第三个参数).如果您不更改该状态,则将重新执行同一条机器指令,并且由于未更改任何内容,因此会发生相同的错误,并且内核会再次发送相同的SIGSEGV信号.
The insight is that a SIGSEGV handler is entered with the program counter set to the faulting machine instruction. When you return from a SIGSEGV handler, the registers are in the state given to it (the pointer ucontext_t
as the third argument of your sa_sigaction
function passed to sigaction
). If you don't change that state, the same machine instruction is re-executed, and since you didn't change anything the same fault happens and the same SIGSEGV signal is sent again by the kernel.
BTW是 Ravenbrook MPS 垃圾收集库.它们的写屏障(用GC的话来说)是通过处理SIGSEGV实现的.这是非常聪明的(并且是非便携式的)代码.
BTW, a nice example of a software handling cleverly and non-portably the SIGSEGV is the Ravenbrook MPS garbage collection library. Their write barrier (in GC parlance) is implemented by handling SIGSEGV. This is very clever (and non portable) code.
NB:实际上,如果您只想显示回溯信息,则可以通过SIGSEGV
处理程序来实现(例如,使用 backtrace(3),然后 _exit(2) -ing,而不是从您的SIGSEGV
信号处理程序返回);它不是完美的,并且不会一直有效-例如如果您损坏了内存堆-因为您将调用非异步信号安全函数,但是在实践中效果很好.最近的GCC正在这样做(在编译器内,例如cc1plus
及其插件),这很有帮助.
NB: in practice, if you just want to display backtrace information, you could do it from a SIGSEGV
handler (e.g. by using GCC libbacktrace or backtrace(3) then _exit(2)-ing instead of returning from your SIGSEGV
signal handler); it is not perfect and won't always work -e.g. if you corrupted the memory heap- because you will call non async-signal-safe functions, but in practice works well enough. Recent GCC is doing that (inside the compiler e.g. cc1plus
and its plugins), and it helps a lot.
这篇关于为什么不能使用signalfd捕获SIGSEGV?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!