信号处理

发布于 2024-10-15 10:05:50 字数 1024 浏览 3 评论 0原文

我只是在 Mac OS X 中使用信号。

为什么在我的信号处理程序完成后以下代码不会产生 SIGSEGV 的默认行为? 在Linux下,代码运行良好。

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void err_crash();

void my_signal_handler (int signal)
{
    fprintf(stderr, "signal == %i\n", signal);
    fflush(stderr);
    err_crash();
}

void err_crash()
{
    static int count= 0;
    if (count)
        signal(SIGSEGV, SIG_DFL);       /* break recursion loop if we recursed */
    count++;

    // one of the two should really crash ;)
    ((void)(*((int volatile *)NULL)));
    *((int *)NULL) = 42;

    abort();                            /* in case we're not crashed yet... */
}

int main ()
{
    signal(SIGSEGV, my_signal_handler);
    err_crash();
    return 0;
}

编辑: 我得到的输出如下:

bonecrusher:devel sw$ g++ signal_problems.cpp -o foo
bonecrusher:devel sw$ ./foo 
signal == 11
^C
bonecrusher:devel sw$

问题是我希望程序在 signal == 11 输出后终止,但它会永远运行,我必须中断它。

I'm just playing with signal in Mac OS X.

Why does the following code not produce the default behavior of SIGSEGV after my signal handler has finished?
Under Linux, the code works fine.

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void err_crash();

void my_signal_handler (int signal)
{
    fprintf(stderr, "signal == %i\n", signal);
    fflush(stderr);
    err_crash();
}

void err_crash()
{
    static int count= 0;
    if (count)
        signal(SIGSEGV, SIG_DFL);       /* break recursion loop if we recursed */
    count++;

    // one of the two should really crash ;)
    ((void)(*((int volatile *)NULL)));
    *((int *)NULL) = 42;

    abort();                            /* in case we're not crashed yet... */
}

int main ()
{
    signal(SIGSEGV, my_signal_handler);
    err_crash();
    return 0;
}

EDIT:
The output I get is the following:

bonecrusher:devel sw$ g++ signal_problems.cpp -o foo
bonecrusher:devel sw$ ./foo 
signal == 11
^C
bonecrusher:devel sw$

The problem is that I want that the program terminates after the output of signal == 11, but it rans forever and I have to interrupt it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

西瑶 2024-10-22 10:05:50

这实际上让我的大脑冻结了几分钟,并且在这个时代永远不应该使用 signal() 的原因在我心中变得更加强烈。

首先,来自 signal() 的手册页

signal() 的行为因情况而异
UNIX 版本也多种多样
历史上有不同版本
Linux 的。避免使用:使用
sigaction(2) 代替。

再往下:

  • 如果将处置设置为函数,则首先
    配置重置为 SIG_DFL,或者
    信号被阻挡(参见便携性
    如下),然后调用处理程序
    带有参数符号。如果调用
    处理程序导致信号是
    被阻塞,则信号被解除阻塞
    从处理程序返回后。

在原始的 Unix 系统中,当安装处理程序时,处置被重置为 SIG_DFL,阻止相同类型的传入信号,然后运行处理程序函数。 System V 提供了这一点,Linux 内核也做了同样的事情。

这意味着,一旦代码在linux系统上运行,一旦调用第二个异常,就会直接退出。

现在到有趣的部分了。 BSD 试图改进这种行为。再次从手册页中:

在 BSD 上,当信号处理程序是
调用时,信号处理不是
重置,以及进一步的实例
信号被阻止
当处理程序正在交付时
正在执行。

而且由于 mac osx 部分基于 BSD,因此一旦代码在 mac osx 上运行,一旦调用第二个异常,它将处于待处理状态并等待第一个异常的处理程序退出。但由于你永远不会退出,所以你陷入了僵局。

这就是为什么应该使用 sigaction() 而不是 signal()

现在有一些提示:

处理程序应该很短,并且返回得很快。如果您正在执行计算并调用其他函数,您可能做错了什么。信号不能替代事件驱动框架。

调用非异步安全的函数是不好的。考虑一下,如果在调用 fprintf 期间发生异常,并且在处理程序内部再次调用 fprintf,会发生什么情况。信号处理程序和程序数据都可能被损坏,因为它们在流本身上运行。

更多阅读:信号处理程序中的“Do”和“Don't”

This actually caused me brain freeze for a few minutes, and the reason why one should never use signal() in this day and age only grew stronger in me.

First of all, from the man pages for signal()

The behavior of signal() varies across
UNIX versions, and has also varied
historically across different versions
of Linux. Avoid its use: use
sigaction(2) instead.

and further down :

  • If the disposition is set to a function, then first either the
    disposition is reset to SIG_DFL, or
    the signal is blocked (see Portability
    below), and then handler is called
    with argument signum. If invocation
    of the handler caused the signal to be
    blocked, then the signal is unblocked
    upon return from the handler.

In the original Unix systems, when a handler was installed, the disposition was reset to SIG_DFL, did not block incoming signals of the same type, and then it ran the handler function. System V provided this, and the linux kernel does the same.

This means that, once the code is run on a linux system, once second exception is called, it will exit directly.

Now to the fun part. BSD tried to improve this behaviour. From the man pages again:

On BSD, when a signal handler is
invoked, the signal disposition is not
reset, and further instances of the
signal are blocked from being
delivered while the handler is
executing.

And since mac osx is partly based on BSD, once the code is run on a mac osx, once second exception is called, it will be pending and wait for the handler of the first exception to exit. But since you will never exit, you have a deadlock.

Thats why one should use sigaction() instead and never signal().

Now to some tips:

Handlers should be short, and return quickly. If you are performing calculations and calling other functions you are probably doing something wrong. Signals are not a substitute for an event driven framework.

Calling functions that are not async-safe is bad. Consider what would happen if an exception happened during a call to fprintf, and inside the handler fprintf was called again. Both the signal handlers and the programs data could be corrupted since they operate on the stream itself.

Some more reading : "Do" and "Don't" inside A Signal Handler

江南烟雨〆相思醉 2024-10-22 10:05:50

根据 POSIX

如果任何 SIGFPE、SIGILL、SIGSEGV 或 SIGBUS 信号在被阻塞时生成,则结果未定义,除非该信号是由 kill() 函数生成的,sigqueue() 函数,或 raise() 函数。

由于 SIGSEGV 在 SIGSEGV 信号处理程序中被阻止,因此在这种情况下结果未定义,并且任何行为都是有效的。如果您不希望它被阻止,您可以使用 sigaction()SA_NODEFER 标志,或使用 sigprocmask() 在信号处理程序中解锁信号。

According to POSIX:

If any of the SIGFPE, SIGILL, SIGSEGV, or SIGBUS signals are generated while they are blocked the result is undefined, unless the signal was generated by the kill() function, the sigqueue() function, or the raise() function.

Because SIGSEGV is blocked while in the SIGSEGV signal handler, the result is undefined in this case and any behavior is valid. If you don't want it to be blocked you could install the signal handler using sigaction() with the SA_NODEFER flag, or use sigprocmask() to unblock the signal within the signal handler.

清风疏影 2024-10-22 10:05:50

在调试器中运行它并单步执行您期望崩溃的指令。

无法保证写入无效地址一定会产生分段错误。也许 Mac OS X 为您映射了这些地址,并且您正在覆盖一些良性的内容。

Run it in a debugger and single-step the instructions that you expect to crash.

There is no guarantee that writing to invalid addresses must create a segmentation fault. Perhaps Mac OS X maps those addresses for you, and you're overwriting something benign.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文