“rebo​​ot”命令会让我退出 shell 还是强制终止 shell?

“rebo​​ot”命令会让我退出 shell 还是强制终止 shell?

在 Linux 下,如果我从 shell 发出reboot命令,导致 shell(例如bash)断开连接的事件顺序是什么?

我认为是以下之一,但不确定是哪一个:

  1. 它在发送重新启动信号之前将我从 shell 中注销。
  2. 它发送重新启动信号,然后 shell 将我注销。
  3. 它会发送重新启动信号,并且 shell 会突然终止,而无需执行注销过程。

答案1

这种行为可能会根据发行版/shell/配置而有所不同。在我的系统(Gentoo/SysVinit)上,当我运行reboot(可能是你的场景3)时会发生以下情况:

重新启动命令委托关闭(来自man reboot):

如果在系统不处于运行级别 0 或 6 时(换句话说,当系统正常运行时)调用halt 或reboot,则将调用shutdown(使用-h 或-r 标志)。有关详细信息,请参阅 shutdown(8) 联机帮助页。

所有进程(包括 shell)都会收到一个 SIGTERM,允许 3 秒进行清理(来自man shutdown):

所有进程首先都会收到信号 SIGTERM 通知系统即将关闭。这使得像 vi(1) 这样的程序有时间保存正在编辑的文件,邮件和新闻处理程序有机会干净地退出等等。

-t sec 告诉 init(8) 在更改到另一个运行级别之前,在向所有进程发送警告 (SIGTERM) 和终止信号 (SIGKILL) 之间等待 sec 秒。如果未指定值,则这两个信号之间的默认时间为三秒。警告:当 shutdown 调用 init 执行关闭(默认行为)时,init 会检查是否所有进程都已终止,并且一旦其子进程全部终止,就会提前停止等待。当使用 -n 标志调用 shutdown 时,即使所有其他进程都已终止,它也会等待指定的完整时间(或三秒)。

Bash 实际上忽略了 SIGTERM(来自man bash):

当 bash 是交互式的时,在没有任何陷阱的情况下,它会忽略 SIGTERM (因此 Kill 0 不会杀死交互式 shell)

因此init更改运行级别,(可能)发送另一个 SIGTERM,然后发送 SIGKILL (来自man init):

当 init 被请求更改运行级别时,它会向新运行级别中未定义的所有进程发送警告信号 SIGTERM。然后等待 3 秒,然后通过 SIGKILL 信号强制终止这些进程。

太长了; bash 将在没有正常关闭的情况下退出,其他程序可能会退出,但我不会依赖任何特定行为。

答案2

正如其他人已经指出的那样,这在很大程度上取决于您对“Linux”是什么这个复杂的哲学问题的回答:)。

是的,在这种情况下,就像在许多其他情况下一样,“GNU/Linux”模因突然变得非常重要和相关。

现在让我们首先看看实际的“内核真相”,即该机制如何真正发挥作用。

就正常内核而言,只要 PID1 运行,一切都很好。但是如果 PID1 崩溃,无论机器的硬件/软件多么健康,这都会立即导致内核恐慌,这相当于用 SIGKILL 杀死所有进程,或者与立即断电具有大致相同的效果。

如果我没记错的话,大多数库存 Linux 默认情况下都会卡在这里,在控制台上打印 PANIC 消息,等待 CPU 停止,直到操作员到来并物理地硬重置/关闭机器(或者虚拟地,如果是虚拟机)。某些 BDS 默认会在此状态下等待 15 秒,然后自行重新启动。

所以这是事情的一部分。

另一部分是特殊的 Linux 内核系统调用,称为reboot()。该系统调用只能由 root 用户进程访问,并允许您控制内核行为:它可以从当前内核 kexec 到链中的下一个内核,物理重新启动/重置机器,物理关闭机器电源,物理停止机器并最终挂起(休眠)机器。

除了hibernate(我不确定那个)之外,上面提到的任何操作都相当于第一种情况下的内核恐慌操作(即所有正在运行的进程都立即被终止,就像断电一样),唯一的区别是kexec将立即切换控制链中的下一个内核,而重新启动、断电和暂停将重新启动所有 cpu、关闭整个计算机的电源或停止其中的所有 cpu,而不会引起恐慌:)。

暂停状态基本上是所有软件都关闭的状态,相当于您在主板增加电路以自动关闭电源之前关闭旧计算机的方式(“您现在可以安全地关闭计算机”)。

需要认识到的重要部分是,启动reboot() 时,PID1/init 必须正在运行。

现在您知道这两个控件确实非常简单。您现在还了解,在某些用户命令 shutdown 和 restart() 系统调用之间发生的所有事情都完全依赖于发行版。

如何处理这个序列通常取决于发行版使用的 init 包。在大多数现代发行版上,这是由 systemd 处理的,它的 PID1 从rebootpoweroffshutdown命令接收控制命令(这些命令应该真正命名为systemd-rebootsystemd-poweroffsystemd-shutdown,因为它们不与其他 init 一起工作)并执行相应的操作。

你可以在 systemd 作者网站上的某个地方挖掘关闭协议是如何实现的,我记得读过这篇文章(请持保留态度,因为 systemd 是不断变化的,有关它的大部分深层信息来自大约 2012~15 年的时间范围,而且确实已经过时了) 。

现在我们正在进入更加奇异的领域。

@rhellen 为您提供了蹩脚的 sysv init poweroff dance 的一些简化解释。

runit基于的发行版具有完全不同的机制,基于系统的nosh机制也是如此s6

所以真正的答案是这在很大程度上取决于你的分布。

例如,在一些古老的基于 sysv 的 Linux 系统上rebootpoweroff命令相当于它们的原始系统调用对应项(我认为在 BSD 上也是如此),因此新手管理员,至少是一个典型的 systemd 用户,有很强的能力杀死整个盒子“断电”方式” :)。与 Solaris 上的故事类似killall

幸运的是,shutdown在几乎所有平台上总是以相同的方式工作,因此当有疑问时,请始终使用该平台。

相关内容