有时在Linux中编写程序时,如果由于某种错误导致崩溃,它将变成不可中断的进程,并继续运行直到我重启计算机(即使我注销)。我的问题如下:
- 是什么原因导致进程变成不可中断?
- 如何防止这种情况发生?
- 这可能是个蠢问题,但是否有任何方法可以在不重新启动计算机的情况下中断它?
有时在Linux中编写程序时,如果由于某种错误导致崩溃,它将变成不可中断的进程,并继续运行直到我重启计算机(即使我注销)。我的问题如下:
一个不可中断的进程是指正在进行系统调用(内核函数)的进程,该调用无法被信号中断。
要理解这是什么意思,您需要了解可中断系统调用的概念。经典的例子是 read()
。这是一个可能需要很长时间(几秒钟)的系统调用,因为它可能涉及到启动硬盘或移动磁头等操作。在大部分时间里,进程将会睡眠,阻塞在硬件上。
当进程在系统调用中睡眠时,它可以接收到 Unix 异步信号(比如 SIGTERM),然后会发生以下情况:
从系统调用中提前返回使得用户空间代码能够立即根据信号改变其行为。例如,在响应 SIGINT 或 SIGTERM 时进行清理终止。
另一方面,有些系统调用不允许以这种方式中断。如果系统调用由于某种原因而停滞不前,则该进程可能会无限期地保持在这种不可杀死的状态中。
LWN在7月份发表了一篇涉及此主题的好文章。
回答原始问题:
如何防止这种情况发生:找出导致问题的驱动程序,然后停止使用它,或者成为内核黑客并修复它。
如何在不重新启动计算机的情况下终止一个不可中断的进程:以某种方式使系统调用终止。通常,在不使用电源开关的情况下最有效的方法是拔掉电源线。您也可以成为内核黑客,并使驱动程序使用 TASK_KILLABLE,如 LWN 文章所述。
当一个进程处于用户模式时,它可以在任何时候被中断(切换到内核模式)。当内核返回用户模式时,它会检查是否有任何未处理的信号(包括用于杀死进程的信号,如SIGTERM
和SIGKILL
)。这意味着只有在返回用户模式时才能杀死进程。
进程不能在内核模式下被杀死的原因是,它可能会破坏同一台机器上所有其他进程使用的内核结构(就像杀死线程可能会潜在地破坏同一进程中其他线程使用的数据结构一样)。
当内核需要做某些可能需要很长时间的事情(例如等待另一个进程写入管道或等待硬件执行某些操作),它会通过标记自身为睡眠状态并调用调度程序来切换到另一个进程进行睡眠(如果没有非睡眠进程,则切换到“虚拟”进程,告诉CPU稍微慢一点并进入循环,即空闲循环)。
如果发送信号给正在睡眠的进程,则必须先将其唤醒,然后才能返回到用户空间,从而处理未处理的信号。这里我们有两种主要类型的睡眠之间的区别:
TASK_INTERRUPTIBLE
,可中断睡眠。如果任务被标记为此标志,则它正在睡眠,但可以通过信号唤醒。这意味着标记任务为睡眠的代码预计可能会收到信号,并在唤醒后检查并从系统调用返回。信号处理完成后,系统调用有可能会自动重新启动(我不会详细介绍如何工作)。TASK_UNINTERRUPTIBLE
,不可中断睡眠。如果任务被标记为此标志,则它不希望被任何东西以外的东西唤醒,除非它在等待的内容,因为它不能轻松地重新启动,或者因为程序期望系统调用是原子的。这也可用于众所周知的短暂睡眠。TASK_KILLABLE
(dda a答案链接的LWN文章中提到的)是一种新的变体。
这回答了你的第一个问题。至于你的第二个问题:无法避免不可中断的睡眠,它们是正常的事情(例如,每次进程从/写入磁盘时都会发生);但是,它们应该只持续几分之一秒。如果它们持续更长时间,通常意味着有硬件问题(或设备驱动程序问题,这在内核看起来是相同的),其中设备驱动程序正在等待硬件执行永远不会发生的操作。 这也可能意味着您正在使用NFS并且NFS服务器已关闭(它正在等待服务器恢复;您还可以使用“intr”选项来避免问题)。
最后,您无法恢复的原因与内核等到返回用户模式以传递信号或杀死进程的原因相同:它可能会损坏内核的数据结构(等待可中断睡眠的代码可能会收到告诉它返回用户空间的错误,在那里可以杀死进程;等待不可中断睡眠的代码不期望任何错误)。
不间断进程通常在页面错误后等待I/O。
考虑下面的情况:
在此状态下,进程/任务无法被中断,因为它无法处理任何信号;如果它这样做,另一个页面错误会发生,它会回到先前的状态。
当我说“进程”时,实际上指的是“任务”,在Linux(2.6)下大致对应于“线程”,它可能具有/proc中的单独的“线程组”条目。
在某些情况下,可能需要等待很长时间。这种情况的典型示例是可执行文件或mapped文件位于网络文件系统上并且服务器失败了。如果I/O最终成功,则任务将继续进行。如果最终失败,则任务通常会收到SIGBUS或类似信号。
sudo kill -HUP 1
来终止无法中断的进程。这将重新启动 init 而不会结束正在运行的进程,而在运行此命令后,我的无法中断的进程已经消失了。如果你在谈论一个“僵尸”进程(在ps输出中被指定为“僵尸”),那么这只是进程列表中的一个无害记录,等待有人收集其返回代码,可以安全地忽略它。
请问您如何描述“不可中断进程”?它是否能够幸存于“kill -9”命令并继续运行?如果是这种情况,那么它可能会卡在某个系统调用上,该系统调用又卡在某个驱动程序上,直到重启(有时最好尽快重启)或卸载相关驱动程序(这不太可能发生),您将一直被困在这个进程中。您可以尝试使用“strace”来查找进程卡在哪里,并在未来避免它。