当主进程从 systemd 启动时无法分离子进程
- 2024-11-08 09:04:00
- admin 原创
- 33
问题描述:
我想生成长期运行的子进程,当主进程重新启动或死亡时,这些子进程仍然存在。从终端运行时,这可以正常工作:
$ cat exectest.go
package main
import (
"log"
"os"
"os/exec"
"syscall"
"time"
)
func main() {
if len(os.Args) == 2 && os.Args[1] == "child" {
for {
time.Sleep(time.Second)
}
} else {
cmd := exec.Command(os.Args[0], "child")
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
log.Printf("child exited: %v", cmd.Run())
}
}
$ go build
$ ./exectest
^Z
[1]+ Stopped ./exectest
$ bg
[1]+ ./exectest &
$ ps -ef | grep exectest | grep -v grep | grep -v vim
snowm 7914 5650 0 23:44 pts/7 00:00:00 ./exectest
snowm 7916 7914 0 23:44 ? 00:00:00 ./exectest child
$ kill -INT 7914 # kill parent process
[1]+ Exit 2 ./exectest
$ ps -ef | grep exectest | grep -v grep | grep -v vim
snowm 7916 1 0 23:44 ? 00:00:00 ./exectest child
请注意,父进程被终止后,子进程仍然处于活动状态。但是,如果我像这样从 systemd 启动主进程...
[snowm@localhost exectest]$ cat /etc/systemd/system/exectest.service
[Unit]
Description=ExecTest
[Service]
Type=simple
ExecStart=/home/snowm/src/exectest/exectest
User=snowm
[Install]
WantedBy=multi-user.target
$ sudo systemctl enable exectest
ln -s '/etc/systemd/system/exectest.service' '/etc/systemd/system/multi-user.target.wants/exectest.service'
$ sudo systemctl start exectest
...当我终止主进程时,子进程也会死亡:
$ ps -ef | grep exectest | grep -v grep | grep -v vim
snowm 8132 1 0 23:55 ? 00:00:00 /home/snowm/src/exectest/exectest
snowm 8134 8132 0 23:55 ? 00:00:00 /home/snowm/src/exectest/exectest child
$ kill -INT 8132
$ ps -ef | grep exectest | grep -v grep | grep -v vim
$
我怎样才能让孩子活下来?
在 CentOS Linux 版本 7.1.1503(核心)下运行 go 版本 go1.4.2 linux/amd64。
解决方案 1:
解决方案是添加
KillMode=process
到服务块。默认值为control-group
,表示 systemd 清理所有子进程。
来自man systemd.kill
KillMode= 指定如何终止此单元的进程。以下为控制组、进程、混合、无之一。
如果设置为 control-group,则在单元停止时,此单元的控制组中的所有剩余进程都将被杀死(对于服务:在执行 stop 命令后,如 ExecStop= 所述)。如果设置为 process,则只有主进程本身会被杀死。如果设置为 combined,SIGTERM 信号(见下文)将发送给主进程,而后续的 SIGKILL 信号(见下文)将发送给单元控制组的所有剩余进程。如果设置为 none,则不会杀死任何进程。在这种情况下,在单元停止时只会执行 stop 命令,否则不会杀死任何进程。停止后仍存活的进程将留在其控制组中,并且控制组在停止后将继续存在,除非它为空。
解决方案 2:
我知道的解决这个问题的唯一方法是使用 --scope 参数启动子进程。
systemd-run --user --scope firefox
这里也提到了 KillMode,但是更改 KillMode 也意味着如果主进程崩溃,如果任何子进程仍在运行,systemd 将不会重新启动它。
解决方案 3:
如果您(像我一样)由于某种原因无法更改KillMode
服务,您可以尝试该at
命令(请参阅man)。
您可以安排命令提前 1 分钟运行。请参阅示例:
# this will remove all .tmp files from "/path/" in 1 minute ahead (this task will run once)
echo rm /path/*.tmp | at now + 1 minute
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件