C 语言信号处理程序中的非本地跳转 ?

参考 Exceptions in C with Longjmp and Setjmp 写了下面一段程序:

#include <setjmp.h>
#include <signal.h>
#include <stdio.h>

jmp_buf ex_buf__;

#define TRY do{ if(!setjmp(ex_buf__)) {
#define CATCH } else {
#define ETRY } } while(0)
#define THROW longjmp(ex_buf__, 1)

void sigint_handler(int sig) {
  THROW;
}


int main(void) {
  if (signal(SIGINT, sigint_handler) == SIG_ERR) {
    return 0;
  }

  TRY {
    while (1) {
      printf("Run...\r");
    }
    /* raise(SIGINT); */
  } CATCH {
    printf("KeyboardInterrupt");
  }
  ETRY;

  return 0;
}

本来是希望在程序运行时, 按下 Ctrl-C 后会的到输出: KeyboardInterrupt.

然而实际运行的时候程序崩溃了。 崩溃前的输出依然是 Run....

假如将 while 部分用 raise(SIGINT) 替代可以得到期望的结果。

这是为什么呢 ? 是不是和死循环有什么关系 ?

编译环境:

  • 编译器:

    gcc version 7.2.0 (x86_64-win32-seh-rev1, Built by MinGW-W64 project)
    clang version 5.0.1 (tags/RELEASE_501/final)
    Target: x86_64-pc-windows-msvc
    Thread model: posix

    两个的运行结果是一样的。

  • 操作系统:

    Microsoft Windows 7
阅读 2k
1 个回答

除非浮点数异常,Windows 明令禁止在信号处理函数中调用 longjmp,否则进程将崩溃(异常 C0000029 INVALID_UNWIND_TARGET)。

一定要在 signal handler 中调用 longjmp 的话,要避开 msvcr 的实现库,可改用 gcc 的 __builtin__longjmp,如下

#include <setjmp.h>
#include <signal.h>
#include <stdio.h>

jmp_buf ex_buf__;

void sigint_handler(int sig)
{
    __builtin_longjmp(ex_buf__, 1);
}

int main(void)
{
    if (signal(SIGINT, sigint_handler) == SIG_ERR) {
        return 0;
    }

    if (!__builtin_setjmp(ex_buf__)) {
        while (1) {
            printf("Run...\r");
        }
    } else {
        // 这里不能调用 print() 或 fprintf(stdout, ...)
        fprintf(stderr, "KeyboardInterrupt\n");
    }
    
    return 0;
}

注意:调用 __builtin_longjmp 之后不能再使用 printf,因为信号中断已经破坏它的内部状态。继续往标准输出(stdout)打印,将导致进程崩溃。

当然,最好不要在 signal handler 中使用 longjmp,以及其他非异步安全的函数,以免产生安全漏洞,或各种奇怪问题。

参考资料
[1]. https://docs.microsoft.com/en...
[2]. https://wiki.sei.cmu.edu/conf...

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进