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 运行两次吗?
这段代码也可以吗?
谢谢。
Can someone explain to me why the printf inside myhandle runs twice ?
行为出现OS-dependent。 myhandle
的控件可能根本就不是 return 到 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;
}
您可能需要检查dissamembly才能找到要跳转的PC(即RIP)。对于你的情况,它应该看起来像,
int *a=NULL;
400697: 48 c7 45 f8 00 00 00 movq [=10=]x0,-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 [=10=]x40077c,%edi
4006ad: e8 de fd ff ff callq 400490 <puts@plt>
,异常在0x4006a3,应该设置为0x4006a8,跳转到printf()。或者+2到0x4006a5,也是有效的。
消息转储两次是因为第一次调用时,
context->uc_mcontext.gregs[REG_RIP]++
,设置RIP为0x4006a4,一处无效位置,再触发一次异常
处理这些信号对于继续执行来说一点都不简单。原因是导致信号的指令尚未执行,因此将通过尝试执行失败的指令继续执行。
信号处理程序执行两次(甚至无限期重复)的原因是返回会导致 CPU 重试执行之前导致分段错误的相同操作,如果不做任何更改,它将导致再次出现分段错误。
为了处理这样的信号(SIGSEGV
、SIGFPE
、SIGILL
等),您必须实际更改信号上下文才能解决问题。为此,您需要使用专门为正在使用的 CPU 编写的代码,并且还需要使用编译器特定的行为,因为您需要修改上下文。
this example 我已经按照以下方式修改了您的代码,现在它可以按您的要求工作了。
#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:~/Whosebug$ gcc test1.c
jeegar@jeegar:~/Whosebug$ ./a.out
Before segfault
Signal is 11
I am still alive
关于在评论中形成 OP 的进一步问题。
要在运行时删除此信号的此处理程序
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 ;
}
}
不确定是否有人说过,但是从信号处理程序调用 printf() 不安全,因为它使用了 malloc()。
请阅读更多内容以了解哪些函数对于信号处理上下文是安全的。在 Linux 和 BSD/MacOS 上,另一种方法是在一个特殊的文件描述符上安排信号传递——在 Linux 上,这是通过 signalfd 函数族完成的,然后你可以在正常的事件循环中处理信号。
Michael Kerrisk 关于 Linux 的书在这里是一个很好的参考。
我是这类编程的新手,如果我的问题很琐碎,我深表歉意。 我想做的是在我的程序中引起分段错误,而不是退出程序,我想处理信号并在分段错误后继续执行。我写了一个似乎有效的代码,我只是想确保这是执行此操作的方法。所以这是我的代码。
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 运行两次吗? 这段代码也可以吗?
谢谢。
Can someone explain to me why the printf inside myhandle runs twice ?
行为出现OS-dependent。 myhandle
的控件可能根本就不是 return 到 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;
}
您可能需要检查dissamembly才能找到要跳转的PC(即RIP)。对于你的情况,它应该看起来像,
int *a=NULL;
400697: 48 c7 45 f8 00 00 00 movq [=10=]x0,-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 [=10=]x40077c,%edi
4006ad: e8 de fd ff ff callq 400490 <puts@plt>
,异常在0x4006a3,应该设置为0x4006a8,跳转到printf()。或者+2到0x4006a5,也是有效的。
消息转储两次是因为第一次调用时,
context->uc_mcontext.gregs[REG_RIP]++
,设置RIP为0x4006a4,一处无效位置,再触发一次异常
处理这些信号对于继续执行来说一点都不简单。原因是导致信号的指令尚未执行,因此将通过尝试执行失败的指令继续执行。
信号处理程序执行两次(甚至无限期重复)的原因是返回会导致 CPU 重试执行之前导致分段错误的相同操作,如果不做任何更改,它将导致再次出现分段错误。
为了处理这样的信号(SIGSEGV
、SIGFPE
、SIGILL
等),您必须实际更改信号上下文才能解决问题。为此,您需要使用专门为正在使用的 CPU 编写的代码,并且还需要使用编译器特定的行为,因为您需要修改上下文。
this example 我已经按照以下方式修改了您的代码,现在它可以按您的要求工作了。
#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:~/Whosebug$ gcc test1.c
jeegar@jeegar:~/Whosebug$ ./a.out
Before segfault
Signal is 11
I am still alive
关于在评论中形成 OP 的进一步问题。 要在运行时删除此信号的此处理程序
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 ;
}
}
不确定是否有人说过,但是从信号处理程序调用 printf() 不安全,因为它使用了 malloc()。
请阅读更多内容以了解哪些函数对于信号处理上下文是安全的。在 Linux 和 BSD/MacOS 上,另一种方法是在一个特殊的文件描述符上安排信号传递——在 Linux 上,这是通过 signalfd 函数族完成的,然后你可以在正常的事件循环中处理信号。
Michael Kerrisk 关于 Linux 的书在这里是一个很好的参考。