为什么不能使用signalfd捕获SIGSEGV? [英] Why can't capture SIGSEGV using signalfd?

查看:64
本文介绍了为什么不能使用signalfd捕获SIGSEGV?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的系统是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

  • (或多或少可移植)避免从信号处理程序返回(例如,通过调用 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 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).

洞察力是输入SIGSEGV处理程序,并将程序计数器设置为故障机器指令.从SIGSEGV处理程序返回时,寄存器处于给定的状态(指针ucontext_t作为传递给sigactionsa_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处理程序来实现(例如,使用 _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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆