什么是不间断的过程?

有时候我在Linux上编写一个程序,并且由于某种错误而崩溃,这将成为一个不间断的过程,并且会一直运行,直到我重新启动计算机(即使我注销了)。 我的问题是:

  • 什么导致一个过程变得不间断?
  • 我如何阻止这种情况发生?
  • 这可能是一个愚蠢的问题,但有什么办法可以中断它,而无需重新启动我的电脑?

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

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

当进程在系统调用中hibernate时,它可以接收一个unixasynchronous信号(比如说SIGTERM),那么会发生以下情况:

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

早期从系统调用返回使得用户空间代码能够响应于该信号而立即改变其行为。 例如,对SIGINT或SIGTERM的反应干净地终止。

另一方面,一些系统调用不允许以这种方式中断。 如果系统由于某种原因调用暂停,则该过程可以无限期地保持在这个不可驱动的状态。

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

回答原来的问题:

  • 如何防止这种情况发生:找出哪个驱动程序导致您的问题,并停止使用,或成为内核黑客并修复它。

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

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

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

当内核需要做一些需要很长时间的事情(例如等待另一个进程写入的一个pipe道或等待硬件做某事的时候),它会自动将自己标记为睡眠状态并调用调度器切换到另一个进程进程(如果没有非hibernate进程,它会切换到一个“虚拟”进程,告诉CPU放慢一点,并坐在一个循环 – 空闲循环)。

如果一个信号被发送到hibernate过程,它必须在它返回到用户空间之前被唤醒,并且因此处理未决信号。 这里我们有两种主要types的睡眠之间的区别:

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

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

这回答你的第一个问题。 至于你的第二个问题:你不能避免不间断的睡眠,这是一个正常的事情(例如,每当一个进程读/写磁盘时都会发生)。 但是,它们只能持续几分之一秒。 如果持续时间更长,则通常意味着硬件问题(或者与内核看起来相同的设备驱动程序问题),其中设备驱动程序正在等待硬件执行永不会发生的事情。 这也意味着你正在使用NFS和NFS服务器closures(它正在等待服务器恢复;你也可以使用“intr”选项来避免这个问题)。

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

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

考虑这个:

  • 线程试图访问一个不在核心的页面(可以是需求加载的可执行文件,已经被换出的匿名内存的页面,或者需要加载的mmap()文件)一样)
  • 内核现在(试图)加载它
  • 该过程无法继续,直到页面可用。

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

当我说“进程”时,我的意思是“任务”,它在Linux(2.6)下粗略转化为“线程”,在/ proc中可能有也可能没有单独的“线程组”

在某些情况下,可能会等待很长时间。 一个典型的例子就是可执行文件或mmap'd文件位于服务器发生故障的networking文件系统上。 如果I / O最终成功,任务将继续。 如果最终失败,任务通常会得到一个SIGBUS或其他东西。

是否有可能编写一个程序,当系统不处于空闲状态时,启动一个进入TASK_UNINTERUPTIBLE状态的进程,强制收集数据,一旦超级用户退出,等待传输? 这将是黑客检索信息,返回僵尸状态,并通过闲置networking传输信息的金矿。 有人可能会争辩说,这是为所有权力创造一个Blackdoor一种方法,可以根据需要进入和退出任何系统。 我坚信这个漏洞可以通过消除TASK_UNINTERUPTIBLE状态来被封好。

我认为这是对Linux系统的一个严重但微妙的安全问题,通过授予超级用户的权力,这个系统在安全性方面享有盛名。 我正在努力成为一名内核黑客,但是,我认为那里有内核黑客可以解决这个问题。

对于你的第三个问题:我认为你可以通过运行sudo kill -HUP 1来杀死不可中断的进程。 它会重新启动init而不终止正在运行的进程,并且在运行之后,我的不可中断的进程不见了。

你能描述一下什么是“不可中断的过程”吗? 它是否能够幸免于“杀人-9”,并且快乐地继续下去? 如果是这样的话,那么它会卡在一些系统调用中,而这些系统调用被卡在某个驱动程序中,并且在这个过程中被卡住直到重新启动(有时最好很快重启)或卸载相关的驱动程序(这不太可能发生) 。 你可以尝试使用“strace”来找出你的进程卡住的地方,并在将来避免它。

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