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.
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");
}
}
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.
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).
When you are back from that handling, the machine is in the same condition, so the SIGSEGV happens again.
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
ucontext_t
) to your handler installed by sigaction
with SA_SIGINFO
), e.g. by changing some registers, or change the address space (e.g. by calling mmap(2) from inside the handler).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, 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: 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.
You can only block a SIGSEGV
sent by kill(2)
and friends. From the manpage of sigprocmask(2)
:
If SIGBUS, SIGFPE, SIGILL, or SIGSEGV are generated while they are blocked, the result is undefined, unless the signal was generated by kill(2), sigqueue(3), or raise(3)
Since the signal isn't really blocked by sigprocmask
- a prerequisite of signalfd
- signalfd
doesn't work. A simple way to test is to use your program but instead of causing a true segmentation fault, send a signal with kill -11
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With