174

有时,每当我在 Linux 中编写程序并且由于某种错误而崩溃时,它将成为一个不可中断的进程并永远继续运行,直到我重新启动计算机(即使我注销)。我的问题是:

  • 是什么导致进程变得不可中断?
  • 我该如何阻止这种情况发生?
  • 这可能是一个愚蠢的问题,但是有没有办法在不重新启动计算机的情况下中断它?
4

5 回答 5

219

不可中断进程是恰好处于系统调用(内核函数)中且不能被信号中断的进程。

要理解这意味着什么,您需要了解可中断系统调用的概念。经典的例子是read()。这是一个可能需要很长时间(几秒钟)的系统调用,因为它可能涉及旋转硬盘驱动器或移动磁头。在这段时间的大部分时间里,进程将处于休眠状态,在硬件上阻塞。

当进程在系统调用中处于休眠状态时,它可以接收到一个 Unix 异步信号(例如 SIGTERM),然后会发生以下情况:

  • 系统调用过早退出,并设置为将 -EINTR 返回到用户空间。
  • 信号处理程序被执行。
  • 如果进程仍在运行,它会从系统调用中获取返回值,并且可以再次进行相同的调用。

从系统调用中提前返回使用户空间代码能够立即改变其行为以响应信号。例如,干净地终止以响应 SIGINT 或 SIGTERM。

另一方面,一些系统调用不允许以这种方式被中断。如果系统调用由于某种原因停止,进程可以无限期地保持在这种不可杀死的状态。

LWN 在 7 月份发表了一篇很好的文章,触及了这个话题。

要回答原始问题:

  • 如何防止这种情况发生:找出哪个驱动程序给您带来麻烦,然后停止使用,或者成为内核黑客并修复它。

  • 如何在不重新启动的情况下杀死不可中断的进程:以某种方式使系统调用终止。在不敲击电源开关的情况下执行此操作的最有效方法通常是拉电源线。您也可以成为内核黑客,并让驱动程序使用 TASK_KILLABLE,如 LWN 文章中所述。

于 2008-10-21T22:14:55.460 回答
56

当一个进程处于用户模式时,它可以随时被中断(切换到内核模式)。当内核返回到用户模式时,它会检查是否有任何信号未决(包括用于终止进程的信号,例如SIGTERMSIGKILL)。这意味着只有在返回用户模式时才能终止进程。

一个进程不能在内核模式下被杀死的原因是它可能会破坏同一台机器中所有其他进程使用的内核结构(同样的方式杀死一个线程可能会破坏同一进程中其他线程使用的数据结构) .

当内核需要做一些可能需要很长时间的事情时(例如,等待另一个进程写入的管道或等待硬件做某事),它会通过将自己标记为睡眠并调用调度程序切换到另一个来休眠进程(如果没有非睡眠进程,它会切换到一个“虚拟”进程,告诉 cpu 放慢一点并处于一个循环中 - 空闲循环)。

如果一个信号被发送到一个休眠的进程,它必须在它返回到用户空间之前被唤醒,从而处理待处理的信号。在这里,我们有两种主要睡眠类型之间的区别:

  • TASK_INTERRUPTIBLE,可中断的睡眠。如果一个任务被标记了这个标志,它正在休眠,但可以被信号唤醒。这意味着将任务标记为睡眠的代码正在等待一个可能的信号,并且在它唤醒后将检查它并从系统调用返回。处理完信号后,系统调用可能会自动重新启动(我不会详细说明它是如何工作的)。
  • TASK_UNINTERRUPTIBLE,不间断的睡眠。如果一个任务被标记了这个标志,除了它正在等待的任何东西之外,它不会被其他任何东西唤醒,要么是因为它不容易重新启动,要么是因为程序期望系统调用是原子的。这也可用于已知非常短的睡眠。

TASK_KILLABLE(在 ddaa 的答案链接到的 LWN 文章中提到)是一个新变体。

这回答了你的第一个问题。至于您的第二个问题:您无法避免不间断的睡眠,它们是正常的事情(例如,每次进程从磁盘读取/写入磁盘时都会发生这种情况);但是,它们应该只持续几分之一秒。如果它们持续更长时间,通常意味着硬件问题(或设备驱动程序问题,在内核看来是一样的),设备驱动程序正在等待硬件做一些永远不会发生的事情。这也可能意味着您正在使用 NFS 并且 NFS 服务器已关闭(它正在等待服务器恢复;您也可以使用“intr”选项来避免该问题)。

最后,您无法恢复的原因与内核等待返回用户模式以传递信号或终止进程的原因相同:它可能会破坏内核的数据结构(等待可中断睡眠的代码可能会收到一个错误,告诉它返回用户空间,该进程可以被杀死;等待不间断睡眠的代码不会出现任何错误)。

于 2008-10-22T00:16:43.467 回答
29

不可中断进程通常在页面错误后等待 I/O。

考虑一下:

  • 线程尝试访问不在核心中的页面(请求加载的可执行文件、已换出的匿名内存页面或请求加载的 mmap() 文件)一样)
  • 内核现在(试图)加载它
  • 在页面可用之前,该过程无法继续。

进程/任务在这种状态下不能被中断,因为它不能处理任何信号;如果确实如此,则会发生另一个页面错误,并且会回到原来的位置。

当我说“进程”时,我真正的意思是“任务”,在 Linux (2.6) 下大致翻译为“线程”,在 /proc 中可能有也可能没有单独的“线程组”条目

在某些情况下,它可能会等待很长时间。一个典型的例子是可执行文件或 mmap 文件位于服务器出现故障的网络文件系统上。如果 I/O 最终成功,任务将继续。如果最终失败,任务一般会得到一个 SIGBUS 什么的。

于 2008-10-22T21:22:46.687 回答
1

对于您的第三个问题:我认为您可以通过运行 sudo kill -HUP 1. 它将重新启动 init 而不会结束正在运行的进程,并且在运行它之后,我的不间断进程就消失了。

于 2017-05-28T19:50:09.647 回答
-3

如果您谈论的是“僵尸”进程(在 ps 输出中被指定为“僵尸”),那么这是进程列表中等待某人收集其返回码的无害记录,可以安全地忽略它。

您能否描述一下您的“不间断过程”是什么?它是否在“kill -9”中幸存下来并愉快地前进?如果是这种情况,那么它会卡在某个系统调用上,该系统调用卡在某个驱动程序中,并且您会一直卡在这个过程中,直到重新启动(有时最好尽快重新启动)或卸载相关驱动程序(这不太可能发生) . 您可以尝试使用“strace”来找出您的流程卡在哪里并在将来避免它。

于 2008-10-21T22:07:19.093 回答