![](/img/trans.png)
[英]What is the use of ignoring `SIGCHLD` signal with `sigaction(2)`?
[英]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重試執行導致分段錯誤的相同事情,並且如果沒有任何改變,它將再次導致分段錯誤。
為了處理這樣的信號( SIGSEGV
, SIGFPE
, SIGILL
等),您必須實際改變信號上下文以解決問題。 為此,您需要使用專為正在使用的CPU編寫的代碼,並使用編譯器特定的行為,因為您需要修改上下文。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.