将信号设施视为中断,由OS(而不是硬件)实现。
由于您的程序快速遍历其根据main()的执行位置,这些中断可能会发生,导致程序被调度到向量(处理程序),在那里运行代码,然后返回到它所在的位置中断。
这些中断(信号)可以来自各种来源,例如硬件错误,如访问错误或未对齐的地址,子进程死亡,用户使用kill
命令生成的信号,或使用kill
系统调用的其他进程。消耗信号的方式是指定它们的处理程序,这些处理程序在信号发生时由OS分派。请注意,其中一些信号无法处理,导致该过程死亡。
但那些可以处理的东西可能非常有用。您可以将它们用于进程间通信,即一个进程将信号发送到另一个处理它的进程,并在处理程序中执行一些有用的操作。如果您向他们发送正确的信号,许多守护进程会执行有用的操作,例如重新读取配置文件。
上述所有语句中未解决的一些问题是多核,在接收信号时在内核空间运行,在接收信号时在内核空间中休眠,系统调用重启和信号处理器延迟。
以下是需要考虑的几个问题:
- 如果内核知道需要将信号传递给在CPU_X上运行的进程X,但内核在CPU_Y(CPU_X!= CPU_Y)上运行时获知它,该怎么办?因此内核需要阻止进程在不同的核心上运行。
- 如果进程在接收信号时在内核空间中运行怎么办?每次进程进行系统调用时,它都会进入内核空间,并在内核空间中进行数据结构和内存分配。所有这些黑客攻击都发生在内核空间吗?
- 如果进程在内核空间中等待其他事件,该怎么办? (读,写,信号,轮询,互斥只是一些选择)。
数目:
- 如果进程在另一个CPU上运行,则内核通过跨CPU通信将向另一个CPU发送中断并为其发送消息。另一个CPU将在硬件中保存状态并跳转到另一个CPU上的内核,然后在另一个CPU上执行信号传递。这是尝试不在另一个CPU上执行进程的信号处理程序的一部分,这将打破缓存局部性。
- 如果进程在内核空间中运行,则不会中断。相反,记录此过程已收到信号。当进程退出内核空间时(在每次系统调用结束时),内核将设置trampoline来执行信号处理程序。
- 如果进程在内核空间中运行,在收到信号后,到达睡眠函数,则该睡眠函数(这对内核中的所有睡眠函数都是通用的)将检查进程是否有待处理的信号。如果是这样,它将不会使进程进入休眠状态,而是取消进入内核时已完成的所有操作,并在设置蹦床以执行信号处理程序然后重新启动系统时将退出到用户空间呼叫。您实际上可以控制要中断系统调用的信号以及不使用
siginterrupt(2)
系统调用的信号。当您使用带有sigaction(2)
标志的SA_RESTART
注册信号时,您可以决定是否要为某个信号重新启动系统调用。如果发出系统调用并被信号截断并且未自动重新启动,则将获得EINTR
(中断)返回值,您必须处理该值。您还可以查看restart_syscall(2)
系统调用以获取更多详细信息。
- 如果进程已经在内核空间中休眠/等待(实际上所有休眠/等待总是在内核空间中),它会从睡眠中唤醒,内核代码会自行清理并在返回用户空间后跳转到信号处理程序如果用户需要,系统调用会自动重启(非常类似于先前对内核空间中进程运行时会发生什么的解释)。
关于为什么所有这一切都如此复杂的一些注释:
- 您不能只停止在内核空间中运行的进程,因为内核开发人员分配内存,为数据结构做些事情等等。如果你只是取消控制,你将破坏内核状态并导致机器挂起。必须以受控方式通知内核代码,它必须停止运行,返回用户空间并允许用户空间处理信号。这是通过内核中所有(几乎所有)休眠函数的返回值完成的。并且内核程序员应该尊重这些返回值并采取相应的行动。
- 信号是异步的。这意味着它们应该尽快交付。想象一个只有一个线程的进程,进入睡眠状态一小时,并发出一个信号。睡眠在内核中。所以你除了内核代码唤醒,清理后,返回用户空间并执行信号处理程序,可能在信号处理程序完成后重新启动系统调用。您当然不希望该过程仅在一小时后执行信号处理程序。然后你期望睡眠恢复。用户空间和内核人员很容易就是这样做的。
- 所有信号都像中断处理程序,但用户空间。这是一个很好的比喻,但并不完美。虽然中断处理程序是由硬件生成的,但一些信号处理程序来自硬件,但大多数只是软件(关于子进程死亡的信号,来自使用
kill(2)
系统调用的其他进程的信号等)。
那么信号处理的延迟是多少?
- 如果当某个其他进程正在运行时发出信号,则由内核调度程序决定是否让其他进程完成其时间片,然后才传递信号。如果您使用的是普通的Linux / Unix系统,这意味着在获得信号之前可能会延迟一个或多个时间片(这意味着毫秒相当于永恒)。
- 当您收到信号时,如果您的进程是高优先级或其他进程已经获得时间片,您将获得非常快的信号。如果你在用户空间中运行,你将立即得到它#34;如果你在内核空间运行,你将很快达到睡眠功能或从内核返回,在这种情况下当你返回到用户空间你的信号处理程序将被召唤。这通常是很短的时间,因为在内核中花费的时间不多。
- 如果你在内核中睡觉,没有其他东西超出你的优先级或需要运行,处理你的系统调用的内核线程被唤醒,在它进入内核的过程中所做的所有事情之后清理,回到用户空间并执行你的信号。这不会花太长时间(在这里说微秒)。
- 如果您运行的是Linux的实时版本并且您的进程具有最高的实时优先级,那么您将在触发后立即获得该信号。谈论50微秒甚至更好(取决于我不能进入的其他因素)。