【问题标题】:How to have Linux wait till my program finishes its SIGTERM action?如何让 Linux 等到我的程序完成其 SIGTERM 操作?
【发布时间】:2019-10-10 17:00:53
【问题描述】:

如何让 Linux 等到我的 C++ 程序完成其清理例程。程序最初调用函数sigaction(2) 来注册一个自定义的 SIGTERM 处理程序。如果通过运行kill -s TERM $(ps -C a.out -o pid=) 来测试处理程序,则没有问题。但是,在真正的关机中却是另外一回事。有时处理程序可以完成所有工作,但有时不能。显然,机器关闭时存在竞争条件。有谁知道如何让系统等待更长的时间以避免竞争条件?谢谢。

【问题讨论】:

  • 这取决于您的 Linux 发行版使用的 init 系统类型:经典 SysVinit、upstartsystemd 或者更不常见的?如果您不知道,请至少告诉您 Linux 发行版的名称和版本。通常,如果您创建适当的配置以在启动时自动启动程序,它还将包括一种指定关闭时等待多长时间的方法。如果你只是手动启动你的程序,在关闭的时候让它死掉,就会被“松散进程的一般处理”处理,这对你的程序来说可能过于严格了。
  • @telcoM 所以快速回答是“不,你不能,除非你在 /etc/rc6.d/ 中放一些东西来用 IPC 关闭你的应用程序并等待它完成。”?
  • 使用systemd时,可以将kill命令指定为ExecStop=命令,然后指定TimeoutStopSec=值来调整超时时间。使用SysVinit,在关闭时杀死松散进程通常发生得很晚,当然只有在任何特定于服务的关闭脚本完成之后,所以只需有一个启动/关闭脚本在关闭过程的早期阶段发送信号并且可以选择使用sleep 或其他东西,以便在程序的关闭脚本退出之前为您的服务提供更多时间。
  • 换句话说,快速回答是“是的,但是实现所需行为的推荐方法取决于所使用的初始化系统。”
  • 也许;这取决于您使用的 Linux 发行版以及它具有的 init 系统。在 RHEL 6.x 中,init.d 脚本需要额外的锁定文件,这会导致关闭脚本跳过手动启动的内容,而无需使用启动脚本。在systemd 中,systemd 工作方式固有的类似行为。 能否请您透露您所询问的 Linux 发行版?

标签: linux sigterm


【解决方案1】:

在您所说的 cmets 中,您使用的是 MX Linux 版本 18.2。它似乎基于 Debian 9,默认使用 systemd,但如果需要,仍然可以选择恢复为经典 SysVinit。 MX Linux 的网页似乎强调 UI,并没有提及 init 系统的任何特殊之处,所以我假设它也使用systemd

对于systemd,一种称为控制组(简称cgroups)的机制正在发挥作用:当systemd 启动一个服务时,它也会将其进程置于一个特殊的cgroup 中。该 cgroup 由服务启动的任何进程自动继承。当 systemd 停止服务时,它会首先执行任何自定义的ExecStop 操作,如果为服务定义了任何操作,则等待TimeoutStopSec,如果服务的 cgroup 中还有任何进程,systemd 将发送它们是一个 SIGTERM,等待另一个 TimeoutStopSec,然后将为 cgroup 中的任何剩余进程发送一个 SIGKILL。

绊倒你的可能是用户会话也被封装在一个 cgroup 中:你手动启动的任何东西,例如sh /etc/init.d/yourservice start 仍然算作您的用户会话的一部分,即使它执行了经典守护进程所需的所有操作。因此,当您启动关闭时,第一个操作是注销所有用户会话...这会导致您手动启动的服务首先收到一个 SIGHUP,然后在短暂延迟后收到 SIGTERM,并且可能在另一个短暂延迟后收到 SIGKILL .清理用户会话后,将执行其余的关闭过程。

为了将init.d 脚本与systemd 一起成功使用,您需要了解一些事项。

systemdinit.d 脚本的兼容性机制通过为每个init.d 脚本自动生成本机systemd .service 单元文件,然后像本机systemd 服务一样使用这些单元文件。这会导致您可能不熟悉的三个要求:

  • 您的init.d 脚本应该在脚本中描述与任何其他服务的依赖关系的任何非注释行之前有一个Linux Standard Base 注释块。它应该看起来像这样(来自 Dovecot IMAP 服务器的示例):
### BEGIN INIT INFO
# Provides:          dovecot
# Required-Start:    $local_fs $remote_fs $network $syslog $time
# Required-Stop:     $local_fs $remote_fs $network $syslog
# Should-Start:      postgresql mysql slapd winbind nslcd
# Should-Stop:       postgresql mysql slapd winbind nslcd
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Dovecot init script
# Description:       Init script for dovecot services
### END INIT INFO
  • 放置init.d 脚本后,您应该运行systemctl daemon-reload 以触发systemd-sysv-generator 的重新运行,该systemd-sysv-generator 生成将调用您的脚本的单元文件。

  • 将脚本设置为/etc/init.d/yourservice 并运行systemctl daemon-reload 后,您应该使用systemctl start yourserviceservice yourservice start 启动服务。只有这些方法会导致systemd 将您的服务置于其自己的控制组中,这对于有序关闭您的服务需求非常重要。运行sh /etc/init.d/yourservice start 不会这样做。

您可以使用systemctl cat yourservice 查看自动生成的yourservice.service 单元文件。

为您的服务编写一个本机 systemd 服务单元文件可能是一个更好的主意。您可以在/lib/systemd/system/ 目录中找到发行版的标准单元文件;您可以将它们用作示例,但您应该将自定义单元文件放入 /etc/systemd/system,以便您的单元文件不会被任何包更新覆盖。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-22
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 2017-01-04
    相关资源
    最近更新 更多