为什么我在下面的 C 代码中得到连续的 SIGSEGV
Why i am getting continuous SIGSEGV in the below C code
我正在尝试学习信号。我知道无效的内存访问会导致段错误。因此,我为 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 信号。我以为我应该只收到一次信号。你们能解释一下为什么我不断收到信号
SEGV 处理程序完成后,触发 re-executes 的指令。由于您没有采取任何措施来防止下一次执行出错,您将再次获得 SEGV,无限次。
更多内容见this answer。
信号处理程序正在返回触发它的指令,即 *a = 5
导致它循环。
您有几个问题,包括在信号处理程序中使用 printf
。
有安全且 not-safe 的处理方法
注意事项
通常不建议使用 signal(2)
进行信号处理。
由于信号语义的工作方式,处理 SIGSEGV
甚至更加复杂。引用手册页:
The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN. The semantics when using signal()
to establish a signal handler vary across
systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.
POSIX.1 solved the portability mess by specifying sigaction(2), which provides explicit control of the semantics when a
signal handler is invoked; use that interface instead of 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)
嗯 :-) 假设您想要使用 signal(2) 并且您想以一种奇怪的方式玩这个 ....
您可以使用 sigjmpset
和 siglongjmp
。
sigjmpset
标记了 siglongjmp
应该跳转到的点。第一次 sigjmpset
被调用(设置点)它 returns 0。当 siglongjmp
跳转到它时,(这意味着它由于长跳转而再次被调用),它 returns 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;
}
我正在尝试学习信号。我知道无效的内存访问会导致段错误。因此,我为 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 信号。我以为我应该只收到一次信号。你们能解释一下为什么我不断收到信号
SEGV 处理程序完成后,触发 re-executes 的指令。由于您没有采取任何措施来防止下一次执行出错,您将再次获得 SEGV,无限次。
更多内容见this answer。
信号处理程序正在返回触发它的指令,即 *a = 5
导致它循环。
您有几个问题,包括在信号处理程序中使用 printf
。
有安全且 not-safe 的处理方法
注意事项
通常不建议使用 signal(2)
进行信号处理。
由于信号语义的工作方式,处理 SIGSEGV
甚至更加复杂。引用手册页:
The only portable use of signal() is to set a signal's disposition to SIG_DFL or SIG_IGN. The semantics when using signal() to establish a signal handler vary across systems (and POSIX.1 explicitly permits this variation); do not use it for this purpose.
POSIX.1 solved the portability mess by specifying sigaction(2), which provides explicit control of the semantics when a signal handler is invoked; use that interface instead of 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)
嗯 :-) 假设您想要使用 signal(2) 并且您想以一种奇怪的方式玩这个 ....
您可以使用 sigjmpset
和 siglongjmp
。
sigjmpset
标记了 siglongjmp
应该跳转到的点。第一次 sigjmpset
被调用(设置点)它 returns 0。当 siglongjmp
跳转到它时,(这意味着它由于长跳转而再次被调用),它 returns 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;
}