简体   繁体   English

Longjmp退出信号处理程序?

[英]Longjmp out of signal handler?

From the question: 从问题:

Is it good programming practice to use setjmp and longjmp in C? 在C中使用setjmp和longjmp是不错的编程习惯?

Two of the comments left said: 其中两条评论说:

"You can't throw an exception in a signal handler, but you can do a longjmp safely -- as long as you know what you are doing. – Dietrich Epp Aug 31 at 19:57 @Dietrich: +1 to your comment. This is a little-known and completely-under-appreciated fact. There are a number of problems that cannot be solved (nasty race conditions) without using longjmp out of signal handlers. Asynchronous interruption of blocking syscalls is the classic example." “你不能在信号处理程序中抛出异常,但你可以安全地做一个longjmp - 只要你知道你在做什么。 - Dietrich Epp 8月31日19:57 @Dietrich:+1你的评论。这是一个鲜为人知且完全不被重视的事实。在不使用信号处理程序的longjmp的情况下,有许多问题无法解决(令人讨厌的竞争条件)。阻塞系统调用的异步中断是典型的例子。“

I was under the impression that signal handlers were called by the kernel when it encountered an exceptional condition (eg divide by 0). 我的印象是内核在遇到异常情况时调用信号处理程序(例如除以0)。 Also, that they're only called if you specifically register them. 此外,如果您专门注册它们,它们只会被调用。

This would seem to imply (to me) that they aren't called through your normal code. 这似乎意味着(对我来说)它们不会通过您的正常代码调用。

Moving on with that thought... setjmp and longjmp as I understand them are for collapsing up the stack to a previous point and state. 继续这个想法...我理解的是setjmp和longjmp用于将堆栈折叠到先前的点和状态。 I don't understand how you can collapse up a stack when a signal handler is called since its called from the Kernel as a one-off circumstance rather than from your own code. 我不明白在调用信号处理程序时如何折叠堆栈,因为它从内核调用作为一次性环境而不是从您自己的代码调用。 What's the next thing up the stack from a signal handler!? 从信号处理程序到堆栈的下一个东西是什么!?

The way the kernel "calls" a signal handler is by interrupting the thread, saving the signal mask and processor state in a ucontext_t structure on the stack just beyond (below, on grows-down implementations) the interrupted code's stack pointer, and restarting execution at the address of the signal handler. 内核“调用”信号处理程序的方式是通过中断线程,将信号掩码和处理器状态ucontext_t在堆栈上的ucontext_t结构中(在下面,在增长的实现中)中断代码的堆栈指针,然后重新启动执行在信号处理程序的地址。 The kernel does not need to keep track of any "this process is in a signal handler" state; 内核不需要跟踪任何“此进程处于信号处理程序”状态; that's entirely a consequence of the new call frame that was created. 这完全是创建新调用框架的结果。

If the interrupted thread was in the middle of a system call, the kernel will back out of the kernelspace code and adjust the return address to repeat the system call (if SA_RESTART is set for the signal and the system call is a restartable one) or put EINTR in the return code (if not restartable). 如果被中断的线程处于系统调用的中间,则内核将退出内核空间代码并调整返回地址以重复系统调用(如果为信号设置了SA_RESTART并且系统调用是可重新启动的)或者把EINTR放在返回码中(如果不能重启)。

It should be noted that longjmp is async-signal-unsafe. 应该注意的是, longjmp是异步信号不安全的。 This means it invokes undefined behavior if you call it from a signal handler if the signal interrupted another async-signal-unsafe function. 这意味着如果从信号处理程序调用它,如果信号中断了另一个async-signal-unsafe函数,它将调用未定义的行为。 But as long as the interrupted code is not using library functions, or only using library functions that are marked async-signal-safe, it's legal to call longjmp from a signal handler. 但只要中断的代码不使用库函数,或者只使用标记为异步信号安全的库函数,从信号处理程序调用longjmp是合法的。

Finally, my answer is based on POSIX since the question is tagged unix . 最后,我的答案基于POSIX,因为问题是标记为unix If the question were just about pure C, I suspect the answer is somewhat different, but signals are rather useless without POSIX anyway... 如果这个问题只是关于纯C的问题,我怀疑答案有些不同,但是如果没有POSIX,信号就毫无用处......

longjmp does not perform normal stack unwinding . longjmp 不执行正常的堆栈展开 Instead, the stack pointer is simply restored from the context saved by setjmp . 相反,堆栈指针只是从setjmp保存的上下文中恢复。

Here is an illustration on how this can bite you with non-async-safe critical parts in your code. 下面是一个说明如何在代码中使用非异步安全的关键部分。 It is advisable to eg mask the offending signal during critical code. 建议例如在关键代码期间屏蔽有问题的信号。

In most systems a signal handler has it's own stack, separate from the main stack. 在大多数系统中,信号处理程序具有自己的堆栈,与主堆栈分开。 That's why you could longjmp out of a handler. 这就是为什么你可以从一个处理程序中解脱出来的原因。 I think it's not a wise thing to do though. 我认为这不是一件明智的事。

值得一读: http : //man7.org/linux/man-pages/man2/sigreturn.2.html关于Linux如何处理信号处理程序调用,在这种情况下它如何管理信号处理程序退出,我读这个建议从信号处理程序执行longjmp()(导致不调用sigreturn())可能充其量只是“未定义”...也必须考虑setjmp()的哪个线程(以及用户堆栈)被调用,并且随后调用了哪个线程(以及用户堆栈)longjmp()!

You can't use longjmp to get out of a signal handler. 你不能使用longjmp来摆脱信号处理程序。

The reason for this is that setjmp only saves the resources (process registers) etc. that the calling-convention specifies that should be saved over a plain function call. 这样做的原因是setjmp只保存了调用约定指定应该通过普通函数调用保存的资源(进程寄存器)等。

When an interrupt occurs, the function being interrupted may have a much larger state, and it will not be restored correctly by longjmp . 当发生中断时,被中断的函数可能具有更大的状态,并且longjmp将无法正确恢复它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM