在信号处理程序中,如何知道程序中断的位置?

时间:2011-07-20 20:54:19

标签: c++ c linux signals interrupt

在x86(64位或32位)Linux上 - 例如:

void signal_handler(int) {
   // want to know where the program is interrupted ...
}

int main() {
    ...
    signal(SIGALRM, signal_handler);
    alarm(5);
    ...
    printf(...); <------- at this point, we trigger signal_handler
    ...
}

在signal_handler中,我们怎么知道我们在main()的printf中被中断?

4 个答案:

答案 0 :(得分:11)

sigaction与sa_flags中设置的SA_SIGINFO一起使用。

原型代码:

#define _GNU_SOURCE 1  /* To pick up REG_RIP */
#include <stdio.h>
#include <signal.h>
#include <assert.h>

static void
handler(int signo, siginfo_t *info, void *context)
{
    const ucontext_t *con = (ucontext_t *)context;

    /* I know, never call printf from a signal handler.  Meh. */
    printf("IP: %lx\n", con->uc_mcontext.gregs[REG_RIP]);
}

int
main(int argc, char *argv[])
{
    struct sigaction sa = { };
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = handler;
    assert(sigaction(SIGINT, &sa, NULL) == 0);
    for (;;);
    return 0;
}

运行它并按 Ctrl-C 。 (使用 Ctrl - \ 终止...)

这适用于x86_64。对于32位x86,请使用REG_EIP代替REG_RIP

[编辑]

当然,如果你实际上是在库函数(如printf)或系统调用(如write)中,那么RIP / EIP寄存器可能指向某个有趣的地方...

您可能希望使用libunwind来抓取堆栈。

答案 1 :(得分:1)

根据您的操作系统/平台,它可能位于以下各个方面:

  • 在当前堆栈上设置了一些深度寄存器
  • 在/中断堆栈上
  • 在与您的信号例程相关的某种回调中......

如果没有其他信息,我认为我们无法进一步追踪这些信息。添加C / C ++标记可能会产生更多响应和视图。

答案 2 :(得分:1)

  

在signal_handler中,我们怎么知道main()中的printf中断了?   你不能,至少不是从C,C ++或POSIX的角度来看。您的操作系统可能会提供特定于操作系统的调用,以便您在堆栈中查找。不过,这有点可疑。

使用基于定时器的信号,哪个指令触发信号就是硬币的折腾。

答案 3 :(得分:0)

解决方法的想法:如果只有少数几个地方可以触发信号处理程序,或者你只对它发生的更大块感兴趣,你可以将它保存在一个变量中。

entered = 1; // or entered = ENTER_PRINTF1;
printf(....);