如何在 Linux 上检测挂起的系统关闭?

Posted

技术标签:

【中文标题】如何在 Linux 上检测挂起的系统关闭?【英文标题】:How to detect pending system shutdown on Linux? 【发布时间】:2011-02-19 09:58:45 【问题描述】:

我正在开发一个需要检测系统关闭的应用程序。 但是,我还没有找到任何可靠的方法来获取有关此事件的通知。

我知道在关闭时,我的应用会收到SIGTERM 信号,然后是SIGKILL。我想知道是否有任何方法可以查询SIGTERM 是否是关机序列的一部分

有人知道是否有办法以编程方式(C API)查询吗?

据我所知,系统没有提供任何其他方法来查询即将关闭的情况。如果是这样,那也可以解决我的问题。我也一直在尝试runlevels,但runlevels 的更改似乎是即时的,并且没有任何事先警告。

【问题讨论】:

有趣的问题。你想停止关机还是只收到通知? 好吧,我已经放弃了。我决定将任何 SIGTERM 视为操作系统想要关闭的消息。我的(蹩脚??)理由是 SIGTERM 的主要目的是礼貌地要求应用程序干净地退出,如果他/她不希望应用程序退出,那么具有足够权限的人不太可能发出 SIGTERM。即使它不是关机,应用程序也应该听它。这让我想到了另一个问题。关闭序列中 SIGTERM 和 SIGKILL 之间的最短时间是多少?我知道可以使用 -t 开关进行配置,但有最低限制吗? 【参考方案1】:

当系统关闭时,会调用rc.d 脚本。

也许您可以在那里添加一个脚本,向您的程序发送一些特殊信号。

但是,我怀疑您是否可以通过这种方式停止系统关闭。

【讨论】:

感谢您的快速回答。我无法控制将部署我的应用程序的机器,因此我无法更改它们的任何内容。另外,我不想停止关机。我只想知道什么时候关机。 好吧,恐怕没有什么可做的了(至少,我能想到的)。也许如果您向我们解释您想要实现的目标,我们可能会帮助您找到替代方案?【参考方案2】:

让您的应用程序对某些 SIGTERM 信号的响应与其他信号不同似乎不透明且可能令人困惑。有争议的是,您应该始终以相同的方式响应给定的信号。添加异常条件会使理解和测试应用程序行为变得更加困难。

添加处理关机的 rc 脚本(通过发送特殊信号)是处理此类问题的完全标准方法;如果此脚本作为标准包(make install 或 rpm/deb 打包)的一部分安装,则无需担心用户机器的控制。

【讨论】:

【参考方案3】:

无法确定SIGTERM 是否是关闭序列的一部分。要检测关机顺序,您可以使用 rc.d 脚本,如 ereOn 和 Eric Sepanson 建议的,或使用 DBus 等机制。

但是,从设计的角度来看,忽略SIGTERM 是没有意义的,即使它不是关机的一部分。 SIGTERM 的主要目的是礼貌地要求 应用程序干净地退出,如果他/她不希望应用程序退出,那么具有足够权限的人不太可能发出SIGTERM

【讨论】:

【参考方案4】:

从人关机:

如果使用时间参数,在系统宕机前 5 分钟 创建/etc/nologin 文件以确保进一步的登录 不允许。

所以你可以测试/etc/nologin 的存在。它不是最优的,但可能是你能得到的最好的。

【讨论】:

【参考方案5】:

可能有点晚了。是的,您可以通过调用 runlevel 命令确定 SIGTERM 是否处于关闭过程中。示例:

#!/bin/bash
trap "runlevel >$HOME/run-level; exit 1" term
read line
echo "Input: $line"

将其保存为term.sh 并运行它。通过执行killall term.sh,您应该能够查看和调查主目录中的run-level 文件。通过执行以下任一操作:

sudo reboot
sudo halt -p
sudo shutdown -P

并比较文件中的差异。然后你应该知道如何去做。

【讨论】:

【参考方案6】:

这有点小技巧,但如果服务器正在运行 systemd,如果你可以运行

/bin/systemctl list-jobs shutdown.target

...它会报告...

JOB UNIT            TYPE  STATE
755 shutdown.target start waiting     <---- existence means shutting down

1 jobs listed.

...如果服务器正在关闭或重新启动(提示:如果你想专门寻找它,有一个 reboot.target )

如果它没有被关闭,你会得到No jobs running.

您必须解析有点混乱的输出,因为 systemctl 不会为两个结果返回不同的退出代码。但它看起来确实相当可靠。但是,如果您更新系统,则需要注意消息中的格式变化。

【讨论】:

【参考方案7】:

我想我明白了。

来源 = https://github.com/mozilla-b2g/busybox/blob/master/miscutils/runlevel.c

我在这里复制部分代码,以防引用消失。

#include "libbb.h"
...
struct utmp *ut;
char prev;

if (argv[1]) utmpname(argv[1]);

setutent();
while ((ut = getutent()) != NULL) 
    if (ut->ut_type == RUN_LVL) 
        prev = ut->ut_pid / 256;
        if (prev == 0) prev = 'N';
        printf("Runlevel: prev=%c current=%c\n", prev, ut->ut_pid % 256);
        endutent();
        return 0;
    

puts("unknown");

【讨论】:

【参考方案8】:

执行您最初想要的实际答案是检查关闭过程(例如 ps aux | grep "shutdown -h" ),然后,如果您想确保检查它的命令行参数并对其计时已启动(例如,在 14:51 开始的“shutdown -h +240”将在 18:51 关闭)。

在一般情况下从整个系统的角度来看没有办法做到这一点。有许多不同的方式可以发生“关闭”。例如,有人可以决定拔掉插头以硬停止他们现在在关机时具有不良/危险行为的程序,或者 UPS 可以首先发送 SIGHUP 然后简单地失败。由于这种关闭可能会突然发生并且在系统的任何地方都没有任何警告,因此无法确保在 SIGHUP 之后继续运行是可以的。

如果一个进程收到 SIGHUP,你基本上应该假设一些更糟糕的事情很快就会发生。如果您想做一些特殊的事情并部分忽略 SIGHUP,那么 a)您需要与任何将执行关闭的程序进行协调,并且 b)您需要做好准备,如果其他系统关闭并在 SIGHUP 后不久将您杀死您的软件和数据将继续存在。写出您拥有的所有数据,并仅继续写入具有安全原子更新的仅附加文件。

对于您的情况,我几乎可以肯定您当前的解决方案(将所有 SIGHUP 视为关闭)是正确的方法。如果你想改进一些东西,你可能应该在关机程序中添加一个功能,通过DBUS 或类似的东西进行通知。

【讨论】:

【参考方案9】:

查看man systemctl,可以这样判断系统是否正在关闭:

if [ "`systemctl is-system-running`" = "stopping" ]; then
    # Do what you need
fi

这是在 bash 中,但您可以使用 C 中的“系统”来完成

【讨论】:

但如果有任何单元降级,我认为这可能会说降级,而不是停止。 其实也可以看一下这个命令的退出值;如果系统处于正常的running 状态,它将以 0 状态退出,如果系统处于任何其他状态,它将返回 > 0 退出值。您可以在 systemctl 手册页上获得所有可能的输出:freedesktop.org/software/systemd/man/systemctl.html - 我已经使用作为 systemd 服务单元运行的 Scala 应用程序测试了此方法,并且它可以工作。

以上是关于如何在 Linux 上检测挂起的系统关闭?的主要内容,如果未能解决你的问题,请参考以下文章

linux 挂起进程

如何检测 Ansible playbook 在执行期间挂起的原因

linux C 当前进程运行10ms后挂起的功能如何实现.

在没有 Root 访问权限的情况下,Linux 可以在 CPL3(用户模式)下崩溃或挂起的情况? [关闭]

如何在不挂起的情况下返回并恢复我的应用程序?

如何在事务上设置挂起的回滚?