unix信号如何工作?

信号如何在unix中工作? 我经历了WR史蒂文斯,但无法理解。 请帮我。

下面的解释并不准确,不同系统之间的工作方式有几个不同(甚至可能在某些部分的不同硬件上使用相同的操作系统),但我认为通常足以满足您的好奇心。使用它们。 大多数人开始在编程中使用信号,甚至没有这种理解水平,但在我习惯使用它们之前,我想了解它们。

信号传递

OS内核有一个称为进程控制块的数据结构,用于运行的每个进程,其中包含有关该进程的数据。 这可以通过进程ID(PID)查找,并包括信号动作和未决信号的表。

当信号发送到进程时,OS内核将查找该进程的进程控制块,并检查信号操作表以找到正在发送的特定信号的操作。 如果信号动作值是SIG_IGN则内核忘记新信号。 如果信号操作值是SIG_DFL则内核在另一个表中查找该信号的默认信号处理操作并执行该操作。 如果值是其他任何值,则假定该过程中的函数地址是信号被发送到的应该被调用的函数地址。 SIG_IGNSIG_DFL的值是SIG_IGN为函数指针的数字,函数指针的值不是进程地址空间中的有效地址(例如0和1,它们都在页0中,从不映射到进程中)。

如果进程注册了信号处理函数(信号操作值既不是SIG_IGN也不是SIG_DFL),则在该信号中为待处理信号表中的一个条目,并且该进程被标记为准备运行(它可能一直在等待某些东西,比如可用于read ,等待信号或其他一些东西的数据。

现在,下一次运行该进程时,OS内核将首先向堆栈添加一些数据,并更改该进程的指令指针,使其看起来几乎就像进程本身刚刚调用了信号处理程序一样。 这不是完全正确的,实际上与实际发生的事情有很大不同,我会稍微谈谈它。

信号处理函数可以做它做的任何事情(它是代表它调用它的过程的一部分,因此它是在知道该程序应该对该信号做什么的情况下编写的)。 当信号处理程序返回时,进程的常规代码再次开始执行。 (再次,不准确,但下一步更多)

好的,上面的内容应该让您对信号如何传递到过程非常了解。 我认为在你掌握完整的想法之前需要这个非常 好的 想法版本,其中包括一些更复杂的东西。

OS内核通常需要知道信号处理程序何时返回。 这是因为信号处理程序采用参数(可能需要堆栈空间),您可以阻止在执行信号处理程序期间传递两次相同的信号,和/或在传递信号后重新启动系统调用。 为了实现这一点,比堆栈和指令指针更改要多一点。

必须发生的是内核需要让进程告诉它已经完成了信号处理函数的执行。 这可以通过将一部分RAM映射到进程的地址空间来完成,该地址空间包含进行此系统调用的代码,并使信号处理函数的返回地址(此函数开始运行时堆栈的最高值)为地址这段代码。 我认为这是在Linux中完成的(至少是较新的版本)。 实现这一目标的另一种方法(我不知道这是否已经完成,但它可能是)将使信号处理函数的返回地址成为无效地址(如NULL),这将导致大多数系统中断,这将再次给予OS内核控制权。 如果发生这种情况并不重要,但是内核必须再次获得控制权来修复堆栈并知道信号处理程序已经完成。

在看到我学到的另一个问题的同时

Linux内核确实将页面映射到此过程,但实际的系统调用注册信号处理程序(sigaction 调用 )采用参数sa_restore参数,该参数应该用作信号的返回地址处理程序,内核只是确保它放在那里。 这个地址的代码发出了 完成的系统调用( sigreturn ),内核知道信号处理程序已经完成。

信号生成

我主要假设你知道信号是如何产生的。 由于发生了某些事情,操作系统可以代表进程生成它们,例如计时器到期,子进程死亡,访问它不应该访问的内存,或发出不应该访问的指令(或者指令不存在)或者是特权的人,或许多其他事物。 定时器shell在function上与其他shell略有不同,因为它可能在进程未运行时发生,因此更像是使用kill系统调用发送的信号。 对于代表当前进程发送的非定时器相关信号,这些信号是在发生中断时生成的,因为当前进程出错了。 此中断提供内核控制(就像系统调用一样),内核生成要传递给当前进程的信号。

将信号设施视为中断,由OS(而不是硬件)实现。

当您的程序快速遍历其根据main()的执行位置时,可能会发生这些中断,导致程序被调度到向量(处理程序),在那里运行代码,然后返回到它被中断的位置。

这些中断(信号)可能来自各种来源,例如硬件错误,如访问错误或未对齐的地址,子进程死亡,用户使用kill命令生成的信号,或来自使用kill系统调用的其他进程。 消耗信号的方式是为它们指定处理程序,这些处理程序在信号发生时由OS分派。 请注意,其中一些信号无法处理,导致该过程死亡。

但那些可以处理的东西可能非常有用。 您可以将它们用于进程间通信,即一个进程将信号发送到另一个处理它的进程,并在处理程序中执行一些有用的操作。 如果您向他们发送正确的信号,许多守护进程会执行有用的操作,例如重新读取配置文件。

以上所有语句中未解决的一些问题是多核,在接收信号时在内核空间中运行,在接收信号时在内核空间中hibernate,系统调用重启和信号处理器延迟。

以下是需要考虑的几个问题:

  • 如果内核知道需要将信号传递给在CPU_X上运行的进程X,但内核在CPU_Y(CPU_X!= CPU_Y)上运行时获知它,该怎么办? 因此内核需要阻止进程在不同的核心上运行。
  • 如果进程在接收信号的同时在内核空间中运行怎么办? 每次进程进行系统调用时,它都会进入内核空间,并在内核空间中进行数据结构和内存分配。 所有这些黑客攻击都发生在内核空间吗?
  • 如果进程在内核空间中hibernate等待其他事件怎么办? (读,写,信号,轮询,互斥只是一些选择)。

回答:

  • 如果进程在另一个CPU上运行,则内核通过跨CPU通信将向另一个CPU发送中断并为其发送消息。 另一个CPU将在硬件中保存状态并跳转到另一个CPU上的内核,然后在另一个CPU上执行信号传递。 这是尝试不在另一个CPU上执行进程的信号处理程序的一部分,这会破坏缓存局部性。
  • 如果进程在内核空间中运行,则不会中断。 而是记录该过程已收到信号。 当进程退出内核空间时(在每次系统调用结束时),内核将设置trampoline来执行信号处理程序。
  • 如果进程在内核空间中运行,在收到信号后,到达睡眠函数,则该睡眠函数(这对内核中的所有睡眠函数来说都是通用的)将检查进程是否有待处理的信号。 如果是这样,它将不会使进程进入hibernate状态,而是取消所有在进入内核时已完成的操作,并在设置蹦床以执行信号处理程序然后重新启动系统时将退出到用户空间呼叫。 您实际上可以控制要中断系统调用的信号以及不使用siginterrupt(2)系统调用的信号。 当您使用带有SA_RESTART标志的sigaction(2)注册信号时,您可以决定是否希望系统调用可以为某个信号重新启动。 如果发出系统调用并被信号切断并且没有自动重启,您将获得EINTR (中断)返回值,您必须处理该值。 您还可以查看restart_syscall(2)系统调用以获取更多详细信息。
  • 如果进程已经在内核空间中hibernate/等待(实际上所有hibernate/等待总是在内核空间中),它从睡眠中被唤醒,内核代码在自身之后清理并且在返回到用户空间之后跳转到信号处理程序,之后系统如果用户如此需要,则会自动重新启动调用(非常类似于先前对内核空间中正在运行的进程所发生的情况的说明)。

关于为什么所有这些都如此复杂的一些注意事项:

  • 您不能只停止在内核空间中运行的进程,因为内核开发人员分配内存,为数据结构做些事情等等。 如果你只是取消控制,你将破坏内核状态并导致机器挂起。 必须以受控方式通知内核代码,它必须停止运行,返回用户空间并允许用户空间处理信号。 这是通过内核中所有(几乎所有)hibernate函数的返回值完成的。 并且内核程序员应该尊重这些返回值,并采取相应的行动。
  • 信号是异步的。 这意味着它们应该尽快交付。 想象一个只有一个线程,进入睡眠状态一小时并发出信号的进程。 睡眠在内核中。 所以你除了内核代码唤醒,清理后自己,返回用户空间并执行信号处理程序,可能在信号处理程序完成后重新启动系统调用。 您当然不希望该过程仅在一小时后执行信号处理程序。 然后你期望睡眠恢复。 用户空间和内核人员都很难做到这一点。
  • 所有信号都像中断处理程序,但用户空间。 这是一个很好的比喻,但并不完美。 虽然中断处理程序是由硬件生成的,但是一些信号处理程序来自硬件,但大多数只是软件(关于子进程死亡的信号,来自另一个进程的信号使用kill(2)系统调用等)。

那么信号处理的延迟是多少?

  • 如果当您收到某个其他进程正在运行的信号时,则由内核调度程序决定是否让其他进程完成其时间片,然后才传递信号。 如果您使用的是常规Linux / Unix系统,这意味着在获得信号之前可能会延迟1个或更多时间片(这意味着毫秒相当于永恒)。
  • 当您收到信号时,如果您的进程是高优先级或其他进程已经获得了时间片,您将获得非常快的信号。 如果您在用户空间中运行,则会“立即”运行它,如果您在内核空间中运行,您将很快到达睡眠函数或从内核返回,在这种情况下,当您返回用户空间时,将调用信号处理程序。 这通常是很短的时间,因为在内核中花费的时间不多。
  • 如果你在内核中睡觉,没有其他东西高于你的优先级或需要运行,处理你的系统调用的内核线程被唤醒,在它进入内核的过程中所做的所有事情之后清理,返回到用户空间并执行您的信号。 这不需要太长时间(在这里谈论微秒)。
  • 如果您运行的是Linux的实时版本并且您的进程具有最高的实时优先级,那么您将在触发后立即获得该信号。 谈论50微秒甚至更好(取决于我不能进入的其他因素)。

信号只是执行过程中的中断。 进程可以发出信号,也可以将信号传递给另一个进程。 也许父母可以向其孩子发送信号以便终止它等。

请查看以下链接以了解。

https://unix.stackexchange.com/questions/80044/how-signals-work-internally

http://www.linuxjournal.com/article/3985

http://www.linuxprogrammingblog.com/all-about-linux-signals?page=show