簡體   English   中英

sigaction 並忽略 linux 環境中 c 的信號

[英]sigaction and ignoring a signal with c in linux environment

我是這種編程的新手,如果我的問題很簡單,我很抱歉。 我想做的是在我的程序中引起分段錯誤,而不是退出程序,我想處理信號並在分段錯誤后繼續執行。 我寫了一個似乎有效的代碼,我只是想確保這是執行此操作的方法。 所以這是我的代碼。

void myhandle(int mysignal, siginfo_t *si, void* arg)
{
  printf("Signal is %d\n",mysignal);

  ucontext_t *context = (ucontext_t *)arg;
  context->uc_mcontext.gregs[REG_RIP]++;
}


int main(int argc, char *argv[])
{
   struct sigaction action;

  action.sa_handler=myhandle;
  sigaction(11,&action,NULL);

  printf("Before segfault\n");

  int *a=NULL;
  int b=*a;

  printf("I am still alive\n");

  return 0;
}

有人可以向我解釋為什么 myhandle 中的 printf 運行兩次嗎? 這段代碼還可以嗎?

謝謝你。

您可能需要檢查dissamembly以找到要跳轉的PC(即RIP)。 對於你的情況,它應該看起來像,

    int *a=NULL;
  400697:       48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
  40069e:       00
    int b=*a;
  40069f:       48 8b 45 f8             mov    -0x8(%rbp),%rax
  4006a3:       8b 00                   mov    (%rax),%eax
  4006a5:       89 45 f4                mov    %eax,-0xc(%rbp)

    printf("I am still alive\n");
  4006a8:       bf 7c 07 40 00          mov    $0x40077c,%edi
  4006ad:       e8 de fd ff ff          callq  400490 <puts@plt>

,異常在0x4006a3上,應設置為0x4006a8以跳轉到printf()。 或者+2到0x4006a5,這也是有效的。

消息轉儲兩次的原因是,當第一次調用時,

context->uc_mcontext.gregs[REG_RIP]++

,它將RIP設置為0x4006a4,一個無效位置,再觸發一個異常。

通過這個例子,我已經以下面的方式修改了你的代碼,現在它可以按你的需要工作。

#include<stdio.h>
#define __USE_GNU
#include<signal.h>
#include<ucontext.h>

void myhandle(int mysignal, siginfo_t *si, void* arg)
{
  printf("Signal is %d\n",mysignal);

  ucontext_t *context = (ucontext_t *)arg;
  context->uc_mcontext.gregs[REG_RIP] = context->uc_mcontext.gregs[REG_RIP] + 0x04 ;
}


int main(int argc, char *argv[])
{

  struct sigaction action;
  action.sa_sigaction = &myhandle;
  action.sa_flags = SA_SIGINFO;

  sigaction(11,&action,NULL);


  printf("Before segfault\n");

  int *a=NULL;
  int b;
  b =*a;

  printf("I am still alive\n");

  return 0;
}

輸出:

jeegar@jeegar:~/stackoverflow$ gcc test1.c
jeegar@jeegar:~/stackoverflow$ ./a.out 
Before segfault
Signal is 11
I am still alive

關於進一步的問題形式OP在評論中。 運行時為此signel刪除此處理程序

void myhandle(int mysignal, siginfo_t *si, void* arg)
{
  printf("Signal is %d\n",mysignal);

if(flag == 0) {
  // Disable the handler
  action.sa_sigaction = SIG_DFL;
  sigaction(11,&action,NULL);
}

if(flag) {
  ucontext_t *context = (ucontext_t *)arg;
  context->uc_mcontext.gregs[REG_RIP] = context-      >uc_mcontext.gregs[REG_RIP] + 0x04 ;
}

}

有人可以向我解釋為什么myhandle里面的printf運行兩次?

該行為似乎取決於操作系統。 來自myhandle的控制可能根本不會返回main

捕獲信號11是很常見的,通常由OS處理以終止程序。

但是,可以為它編寫一個信號處理程序,並讓該函數在exit之前打印出來。

struct sigaction action;
struct sigaction old_action;

void myhandle( int mysignal )
{
    if( 11 == mysignal )
    {
        printf( "Signal is %d\n", mysignal );   // <-- this should print OK.
        sigaction( 11, &old_action, NULL );     // restore OS signal handler, or just exit().
        return;
    }
}


int main(int argc, char *argv[])
{
    action.sa_handler = myhandle;
    sigaction( 11, &action, &old_action );

    printf("Before segfault\n");

    int *a=NULL;
    int b=*a;

    printf( "I am still alive\n" );   // <-- this won't happen
    return 0;
}

不確定是否已經說過,但是從信號處理程序調用printf () 是不安全的,因為它使用了 malloc()。

請閱讀更多內容以了解哪些功能對於信號處理上下文是安全的。 在 Linux 和 BSD/MacOS 上,另一種方法是在特殊的文件描述符上安排信號傳遞——在 Linux 上,這是通過 signalfd 系列函數完成的,然后您可以在正常的事件循環中處理信號。

Michael Kerrisk 關於 Linux 的書在這里是一個很好的參考。

這些信號對於繼續執行來說並不是微不足道的。 原因是導致信號的指令尚未執行,因此嘗試執行失敗指令將繼續執行。

信號處理程序執行兩次(或者甚至無限重復)的原因是返回會導致CPU重試執行導致分段錯誤的相同事情,並且如果沒有任何改變,它將再次導致分段錯誤。

為了處理這樣的信號( SIGSEGVSIGFPESIGILL等),您必須實際改變信號上下文以解決問題。 為此,您需要使用專為正在使用的CPU編寫的代碼,並使用編譯器特定的行為,因為您需要修改上下文。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM