我正在尝试学习信号。我知道无效的内存访问将导致段错误。因此,我为SIGSEGV信号注册了一个信号处理程序。
#include <stdio.h>
#include <signal.h>
void sighandler(int signum)
{
printf("%s\n", __func__);
}
int main()
{
int *a = NULL;
signal(SIGSEGV, sighandler);
*a = 5;
return 0;
}
运行此代码,我不断收到SIGSEGV信号。我以为我应该只收到一次信号。你们能解释为什么我不断收到信号吗
答案 0 :(得分:4)
SEGV处理程序完成后,触发的指令将重新执行。由于您没有采取任何措施来防止下次执行出错,因此您将无限制地再次获得SEGV。
在this answer中查看更多信息。
答案 1 :(得分:2)
信号处理程序返回到触发它的指令,即*a = 5
,这导致它循环。
您有几个问题,包括在信号处理程序中使用printf
。
有安全和不安全的方法来处理此问题
通常不建议使用signal(2)
进行信号处理。
由于信号语义的工作方式,处理SIGSEGV
更加复杂。从手册页引用:
signal()的唯一可移植用法是将信号的处置方式设置为SIG_DFL或SIG_IGN。使用signal()时的语义 建立一个不同的信号处理程序 系统(POSIX.1明确允许这种变化);不要为此目的使用它。
POSIX.1通过指定sigaction(2)解决了可移植性问题,当sigaction(2) 信号处理程序被调用;使用该接口而不是signal()。
因此,您应该做的第一件事就是使用sigaction
。
接下来,处理SIGSEGV是一个奇怪的野兽:
How to write a signal handler to catch SIGSEGV?
和
Does linux allow any system call to be made from signal handlers?
有很好的答案,并深入了解具体细节。那里给出的一些答案中有外部链接。
好吧:-)假设您想要使用signal(2),而您想要以一种怪异的方式玩这个游戏 ....
您可以使用sigjmpset
和siglongjmp
。
sigjmpset
标记了siglongjmp
应该跳转到的位置。第一次调用sigjmpset
(以设置点)时,它返回0。siglongjmp
跳转到它时(这意味着它由于跳远而再次被调用),它返回1。
这意味着我们可以做到:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <setjmp.h>
sigjmp_buf env;
int sigsav;
void sighandler(int signum)
{
const char msg[] = "Skipping signal\n";
write(2, msg, sizeof(msg));
siglongjmp(env, sigsav);
}
int main()
{
int *a = NULL;
signal(SIGSEGV, sighandler);
if(!sigsetjmp(env, sigsav)) {
printf("setting value of a\n");
*a = 5;
}
else {
printf("returned to sigsetjmp, but now we skip it!\n");
}
return 0;
}