【问题标题】:How is signal handled when there is no system call?没有系统调用时如何处理信号?
【发布时间】:2022-11-07 21:38:51
【问题描述】:

我读了几本关于信号的 Linux 书籍和教程,他们都说内核在内核从内核转换到用户模式的时间处理信号。这是完全有道理的,直到我看到并尝试了following code

>cat sig_timing.c
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>

volatile bool done = false;

static void sig_handler(int signo)
{
    printf("Received signal %d (%s), current errno:%s\n", signo, strsignal(signo), strerror(errno));
    done = true;
}

int main(int argc, char **argv)
{
    signal(SIGALRM, sig_handler);
    alarm(3);
    while (!done) {
        strlen("Hello World!");
    }

    return 0;
}

>gcc sig_timing.c
>./a.out
Received signal 14 (Alarm clock), current errno:Success

所以主函数注册信号后进入无限循环,循环没有调用任何系统调用,所以没有机会进入内核,那么就没有从内核态到用户态的转换,那么应该没有机会调用信号处理程序,对吧?

Later on,谈话主持人解释了发生了什么(我适应了一点):

Sender内核线程发送inter-cpu消息导致硬件 中断 CPU 运行目标进程,使其进入 内核处理中断,并返回用户模式。

我不太相信:这个解释似乎是说信号发送器和信号接收器在 2 个 CPU 硬件线程上运行。但是没有超线程的 CPU 呢?该进程仅在一个 CPU 线程上运行。在那种情况下,当用户土地代码运行无限循环时,信号是否有机会得到处理?

【问题讨论】:

  • 当信号发生时,内核可以强制进程进入内核模式。
  • 中断处理程序根本不在“线程”上运行。这是CPU的核心原理;当收到中断时,CPU暂停当前正在执行的线程(我确信它对于多核和超线程 CPU 来说更复杂)并且只运行中断处理程序。处理程序由内核注册;他们可以为所欲为。一个简单的例子是定时器中断。当您运行无限循环时,您的整个操作系统会崩溃吗?不,甚至在没有超线程的单核 CPU 上也不行。那是因为定时器中断处理程序仍然有效,所以调度程序仍然有效。
  • 即使没有 inter-cpu 消息,线程仍然可以在其时间片到期时被调度程序抢占。
  • 内核运行信号处理程序的方式与抢占进程运行另一个处理程序的方式相同:当从时钟接收到中断时,cpu 将运行中断处理程序而不是它正在执行的任何操作。这就是内核接管执行并调用处理程序的方式。

标签: c linux linux-kernel signals


【解决方案1】:

这将是一个复杂的答案,所以请耐心等待。

抢先调度

所有现代操作系统都使用抢先式调度(与协作式调度相比),其中硬件计时器被编程为定期引发计时器中断(称为操作系统“滴答声”),然后进入操作系统调度程序。因此,即使用户线程正在运行一个无限循环,每隔一段时间(可能是 10 毫秒),硬件也会引发异常,从而导致内核陷入陷阱。这允许操作系统运行调度程序,以决定接下来将运行哪个用户任务。在您的具体示例中,这允许内核不仅可以安排您的程序(sig_timing),还可以通知何时应该触发用户空间警报。

抢先调度是即使在单核计算机上也能获得交互式多道程序体验的主要原因。 CPU 在任务之间是分时的,调度程序在每个滴答声中运行以调度不同的任务。

请注意,OS 定时器周期(10ms)远小于用户报警时间(3s),OS 几乎可以在 3s 内将信号传递给进程。另一个相关的后果是,如果有另一个高优先级用户空间任务占用 CPU,操作系统可以在 3 秒后交付 SIGALRM,不允许操作系统调度您的程序。

旁注:合作调度

在协作调度中,任务需要明确地将控制权交给调度程序(通常使用yield-like 调用)。在这样的系统上,您的程序可能永远不会收到信号。

精确的异常和信号

与某些信号(SIGSEGV、SIGILL、SIGBUS)不同,警报信号(SIGALRM)与特定的用户空间指令无关,并且不需要“精确”。换句话说,内核不需要非常精确地传递信号。请记住,对于 CPU 内核来说,一秒钟是非常长的。

然而,精确的异常是由特定指令引起的。 SIGSEGV 由权限错误引起,SIGILL 由非法指令等引起。此类指令导致硬件直接在指令上引发陷阱,操作系统开始运行。操作系统处理故障并将相应的信号转发给应用程序,并准确地显示指令上的状态。

【讨论】:

    猜你喜欢
    • 2013-05-24
    • 1970-01-01
    • 2019-07-30
    • 2014-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-24
    • 1970-01-01
    相关资源
    最近更新 更多