[英]Receiving SIGINT and exception Handles in Linux
Let's say we have a program in C that uses the sleep() function假设我们有一个使用 sleep() 函数的 C 程序
The program executes and goes to sleep.程序执行并进入休眠状态。 Then we type Ctrl + C to send a SIGINT signal to the process.
然后我们键入Ctrl + C向进程发送 SIGINT 信号。
We know that the default action upon receipt of a SIGINT is to terminate the process, we also know that the sleep() function resume the process whenever the sleeping process receives a signal.我们知道收到 SIGINT 后的默认操作是终止进程,我们也知道只要休眠进程收到信号,sleep() 函数就会恢复进程。
And my textbook says in order to allow sleep() function to return, we must install a SIGINT handler like this:我的教科书说为了允许 sleep() 函数返回,我们必须像这样安装一个 SIGINT 处理程序:
void handler(int sig){
return; /* Catch the signal and return */
}
...
int main(int argc, char **argv) {
...
if (signal(SIGINT, handler) == SIG_ERR) /* Install SIGINT handler */
unix_error("signal error\n");
...
sleep(1000)
}
Althouth the code seems to be straightforward, I still have questions if I want to dig deeper:虽然代码看起来很简单,但如果我想深入挖掘,我仍然有疑问:
Background: When the process is sleeping and we type Ctrl + C to send SIGINT背景:当进程正在休眠时,我们键入Ctrl + C发送 SIGINT
Q1-My understanding is, Kernel sends SIGINT to the process by updating the SIGINT's corresponging pending bit in the pend bit vector, is my understanding correct? Q1-我的理解是,内核通过更新挂起位向量中 SIGINT 的相应挂起位来向进程发送 SIGINT,我的理解是否正确?
Q2-The processor detects the existance of SIGINT, but since we overwrite the handler to make it return in stead of terminating the process, so our handler get executed, and then Kernel clears SIGINT's corresponging pending bit, is my understanding correct? Q2-处理器检测到 SIGINT 的存在,但是由于我们覆盖了处理程序使其返回而不是终止进程,所以我们的处理程序被执行,然后内核清除了 SIGINT 的相应挂起位,我的理解是否正确?
Q3- Since SIGINT's corresponging pending bit is cleared, then how can sleep() function gets return? Q3- 既然SIGINT对应的pending位被清除了,那么sleep()函数如何返回呢? I think it should be in sleep still because in theory, sleep() function has no way of knowing the existance of SIGINT(has been cleared)
我认为它应该仍然处于睡眠状态,因为理论上,sleep() 函数无法知道 SIGINT 的存在(已被清除)
Q3- Since SIGINT's corresponging pending bit is cleared, then how can sleep() function gets return?
Q3- 既然SIGINT对应的pending位被清除了,那么sleep()函数如何返回呢?
Imagine the sleep()
function in the kernel as a function that:将内核中的
sleep()
函数想象成一个函数:
0
if the time expired)0
)sleep()
was interrupted by a signal instead of returning to the code that called sleep()
)sleep()
被信号中断时内核返回到信号处理程序,而不是返回到调用sleep()
的代码)Also imagine that there's a second function (that I'm going to call wake()
for no particular reason) that:还想象有第二个函数(我将无特殊原因调用
wake()
):
Naturally, if the timer's IRQ handler notices that the "timer event" has expired then the timer's IRQ handler would call the wake()
function to wake the task up again.当然,如果定时器的 IRQ 处理程序注意到“定时器事件”已经过期,那么定时器的 IRQ 处理程序将调用
wake()
函数再次唤醒任务。
Now imagine there's a third function (that I'm going to call send_signal()
) which might be called by other functions (eg called by kill()
).现在假设有第三个函数(我将调用它
send_signal()
),它可能会被其他函数调用(例如被kill()
调用)。 This function might set a "pending signal" flag for the task that's supposed to receive the signal, then check what state the receiving task is in;这个函数可能会为应该接收信号的任务设置一个“挂起信号”标志,然后检查接收任务处于什么状态; and if the receiving task is in the "SLEEPING" state it calls the
wake()
function to wake it up (and then lets the latter part of the sleep()
function worry about delivering the signal back to user-space whenever the scheduler feels like giving the task CPU time later).如果接收任务处于“睡眠”状态,它会调用
wake()
函数将其唤醒(然后让sleep()
函数的后半部分担心在调度程序感觉时将信号传送回用户空间比如稍后给任务 CPU 时间)。
Q1: the kernel checks if the process has blocked the received signal, if so, it updates the pending signal bit (unreliable, on systems with relable signals, this should be a counter) in the process entry, for the signal handler to be called when signals are unblocked again (see below). Q1:内核检查进程是否阻塞了接收到的信号,如果是,则更新进程入口中的挂起信号位(不可靠,在有可靠信号的系统上,这应该是一个计数器),以便调用信号处理程序当信号再次畅通时(见下文)。 If not blocked, the system call prepares the return value and
errno
value and returns to user mode with a special code installed in the program's virtual stack that makes it to call the signal handler (already in user mode) before returning from the generic syscall
code.如果没有被阻塞,系统调用准备返回值和
errno
值并返回到用户模式,并在程序的虚拟堆栈中安装一个特殊代码,使其在从通用syscall
代码返回之前调用信号处理程序(已经在用户模式) . The return from the system call gives -1
to the caller code, and the errno
variable is set to EINTR
.系统调用的返回将
-1
赋给调用者代码,并将errno
变量设置为EINTR
。 This requires the process to have installed a signal handler, because by default the action is to abort the process, so it will not return from the system call it is waiting on.这需要进程安装信号处理程序,因为默认情况下操作是中止进程,因此它不会从正在等待的系统调用中返回。 Think that when one says the kernel the actual code executed is in the system call being awaken and notified of the special condition (a signal received) The interrupted call, detects that a signal handler is to be called, and prepares the user stack to jump to the proper place (the interrupt handler in user code) before returning from the
syscall()
wrapper.认为当一个人说内核时,实际执行的代码是在系统调用中被唤醒并通知特殊条件(收到信号)中断的调用,检测到要调用信号处理程序,并准备用户堆栈跳转在从
syscall()
包装器返回之前到适当的位置(用户代码中的中断处理程序)。
Q2: pending bit is only used to save that a pending signal handler is to be called, so this is not the case. Q2: pending bit只是用来保存要调用一个pending signal handler,所以不是这样的。 In the execution part of the process, the unix program loader installs some basic code to jump to the signal handler before returning from the system call.
在进程的执行部分,unix 程序加载器安装一些基本代码以在从系统调用返回之前跳转到信号处理程序。 This is because the signal handler has to execute in user mode (not in kernel mode) so everything happens upon termination of system call.
这是因为信号处理程序必须在用户模式下执行(而不是在内核模式下),所以一切都在系统调用终止时发生。 The signal handler executed is the
SIGINT
, but the code interrupted is a system call, and nothing happens until the system call returns (with the return code and the errno
variable already fixed)执行的信号处理程序是
SIGINT
,但中断的代码是系统调用,在系统调用返回之前什么都不会发生(返回代码和errno
变量已经固定)
Q3: well, your reasoning was based on a wrong premise, that is, the interrupt pending flag is indicating that an interrupt has been received . Q3:嗯,你的推理是基于一个错误的前提,即中断挂起标志表示已经接收到中断。 This bit only signals that an unprocessed interrupt has been marked for delivery as soon as you unblock it , and this only happens in another system call (to unblock a signal).
该位仅表示未处理的中断已被标记为在您解除阻塞后立即传递,并且这仅发生在另一个系统调用中(解除阻塞信号)。 As soon as the signal is unblocked, the return code of the
sigsetmask(2)
syscall will execute the signal handler.一旦信号被解锁,
sigsetmask(2)
系统调用的返回代码将执行信号处理程序。 In this case, the signal will be delivered to the process as soon as the timer elapses, the system call will be interrupted and, if you have not installed a signal handler for the SIGALRM
signal (but sleep(2)
implementation does this ---at least, old implementations did) the program will be aborted.在这种情况下,信号将在计时器结束后立即传递给进程,系统调用将被中断,如果您没有为
SIGALRM
信号安装信号处理程序(但sleep(2)
实现会这样做 - - 至少,旧的实现是这样的)程序将被中止。
When I say that the program is aborted by the kernel but in both cases, the signals involved ( SIGINT
and SIGALRM
) don't make it to dump a core file.当我说程序被内核中止时,但在这两种情况下,所涉及的信号(
SIGINT
和SIGALRM
)都不会转储核心文件。 The program is aborted without generating core
.该程序在未生成
core
的情况下中止。 This is different to the behaviour of the abort()
routine, which sends a SIGABRT
and so, it makes de kernel to dump a core file of the process.这与发送
SIGABRT
的abort()
例程的行为不同,它使 de kernel 转储进程的核心文件。
Your understanding is correct.你的理解是正确的。
Think about it.想想看。 The process is blocked in the kernel.
该进程在内核中被阻塞。 We need to return to user space to run the handler.
我们需要返回到用户空间来运行处理程序。 How can we do that without interrupting whatever blocking kernel call was running?
我们如何才能在不中断任何正在运行的阻塞内核调用的情况下做到这一点? We only have one process/thread context to work with here.
我们在这里只有一个进程/线程上下文可以使用。 The process can't be both sleeping and running a signal handler.
该进程不能同时处于休眠状态和运行信号处理程序。
The sequence is:顺序是:
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.