Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
451 views
in Technique[技术] by (71.8m points)

linux - Why can't capture SIGSEGV using signalfd?

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
");
               (*a) = 1;
           } else if (fdsi.ssi_signo == SIGQUIT) {
               printf("Got SIGQUIT
");
               exit(EXIT_SUCCESS);
           } else {
               printf("Read unexpected signal
");
           }
       }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

  • (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 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...