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

fly*_*bin 11 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
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

Nem*_*emo 12

在sa_flags中设置SA_SIGINFO时使用sigaction.

原型代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

运行它并点击Ctrl-C.(Ctrl-\用于终止...)

这适用于x86_64.对于32位x86,请使用REG_EIP而不是REG_RIP.

[编辑]

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

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

  • printf 通常是不可重入的,所以如果你在收到信号时碰巧在 printf 中,这可能会打印垃圾或崩溃...... (2认同)
  • @Chris:是的,我知道.这个示例程序可以自行运行(可能),这是演示原理的最简单方法.但这就是为什么我把评论包括在内并称之为"原型代码":-) (2认同)