鍍金池/ 問答/C  Office/ C 語言信號處理程序中的非本地跳轉 ?

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) 替代可以得到期望的結果。

這是為什么呢 ? 是不是和死循環(huán)有什么關系 ?

編譯環(huán)境:

  • 編譯器:

    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

    兩個的運行結果是一樣的。

  • 操作系統(tǒng):

    Microsoft Windows 7
回答
編輯回答
萌二代

除非浮點數(shù)異常,Windows 明令禁止在信號處理函數(shù)中調用 longjmp,否則進程將崩潰(異常 C0000029 INVALID_UNWIND_TARGET)。

一定要在 signal handler 中調用 longjmp 的話,要避開 msvcr 的實現(xiàn)庫,可改用 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,因為信號中斷已經(jīng)破壞它的內部狀態(tài)。繼續(xù)往標準輸出(stdout)打印,將導致進程崩潰。

當然,最好不要在 signal handler 中使用 longjmp,以及其他非異步安全的函數(shù),以免產生安全漏洞,或各種奇怪問題。

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

2017年8月28日 03:58