如何在systemd控制组之外启动进程

时间:2016-02-04 11:48:30

标签: linux signals systemd

我有一个服务器进程(从systemd启动),可以启动更新过程。更新过程自我守护自身然后(理论上)用SIGTERM杀死服务器。我的问题是SIGTERM传播到更新过程,它是孩子

出于调试目的,更新过程只是休眠,我手动发送kill。

kill之前的PS输出示例:

    1  1869  1869  1869 ?           -1 Ss       0   0:00 /usr/local/bin/state_controller --start
 1869  1873  1869  1869 ?           -1 Sl       0   0:00  \_ ProcessWebController --start
 1869  1886  1869  1869 ?           -1 Z        0   0:00  \_ [UpdateSystem] <defunct>
    1  1900  1900  1900 ?           -1 Ss       0   0:00 /bin/bash /usr/local/bin/UpdateSystem refork /var/ttm/update.bin
 1900  1905  1900  1900 ?           -1 S        0   0:00  \_ sleep 10000

请注意,UpdateSystem位于单独的PGID和TPGID中。 (<defunct>进程是守护进程的结果,并且(我认为)不是问题。)

UpdateSystem是一个bash脚本(虽然我可以很容易地使它成为一个C程序,如果这将有所帮助)。从https://stackoverflow.com/a/29107686/771073获取守护程序代码后,有趣的是:

#############################################
trap "echo Ignoring SIGTERM" SIGTERM
sleep 10000
echo Awoken from sleep - presumably by the SIGTERM
exit 0

当我kill 1869(将SIGTERM发送到state_controller服务器进程时,我的日志文件包含:

Terminating
Ignoring SIGTERM
Awoken from sleep - presumably by the SIGTERM

我真的想阻止SIGTERM被发送到sleep进程。


(实际上,我确实希望阻止它被发送到apt-get upgrade,这会通过道德等同于systemctl stop ttm.service来停止系统,而ExecStop被指定为{ {1}} - 以防万一改变任何人的答案。)

这个问题很相似,但是接受的答案(使用/bin/kill $MAINPID)对我来说效果不好 - 我想杀死一些子进程,而不是更新进程: Can't detach child process when main process is started from systemd

5 个答案:

答案 0 :(得分:2)

一种完全不同的方法是升级过程通过更新/sys/fs/cgroup/systemd文件系统将自己从服务组中删除。特别是在bash中:

echo $$ > /sys/fs/cgroup/systemd/tasks

进程只属于一个控制组。将其PID写入根tasks文件会将其添加到其他控制组,并将其从服务控制组中删除。

答案 1 :(得分:1)

我们决定采取的方法是在单独的(单次)服务中启动更新过程。因此,它自动属于一个单独的控制组,因此终止主服务并不会杀死它。

虽然有一个皱纹。该软件包会安装ttm.servicettm.template.update.service。要运行更新程序,我们将ttm.template.update.service复制到ttm.update.service,运行systemctl daemon-reload,然后运行systemctl start ttm.update.service。为什么要复制?因为当更新程序安装新版本的ttm.template.update.service时,它将强制终止作为该服务运行的任何进程。 KillMode=None似乎提供了一种方法,但是虽然它似乎有效,但是随后调用apt-get会产生一个关于dpkg被中断的令人讨厌的错误。

答案 2 :(得分:1)

您确定系统没有将TERM信号发送到子进程吗?

根据服务类型,如果主进程终止,systemd将执行清理并终止同一cgroup下的所有子进程。

这是由KillMode = property定义的,默认设置为control-group。您可以将其设置为&#34; none&#34;或&#34;过程&#34;。 https://www.freedesktop.org/software/systemd/man/systemd.kill.html

答案 3 :(得分:0)

我和你有相同的意向。

升级过程是父过程的子过程。父进程由服务调用。

要点不是Cgroup,而是MAINPID。

如果使用PIDFILE分隔MAINPID,则当服务类型=分叉时,情况就解决了。

[Service]
Type=forking
PIDFile=/run/test.pid

答案 4 :(得分:0)

我们遇到了完全相同的问题。我们最终要做的是以enter image description here的身份启动更新过程:

systemd-run --unit=my_system_upgrade --scope --slice=my_system_upgrade_slice -E  setsid nohup start-the-upgrade &> /tmp/some-logs.log &

这样,更新过程将在其他cgroup中运行,并且不会终止。此外,我们使用setsid + nohup确保该进程具有自己的组和会话,并且父进程是初始化进程。