从systemd启动主进程时无法分离子进程

我想生成长时间运行的子进程,这些子进程在主进程重新启动/死亡时仍然存在。从终端运行时,这可以正常工作:


$ 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 release 7.1.1503 (Core) 下运行 go version go1.4.2 linux/amd64。


慕标琳琳
浏览 154回答 3
3回答

qq_遁去的一_1

解决方法是添加KillMode=process到服务区。默认值是control-group这意味着 systemd 清理任何子进程。来自man systemd.killKillMode= 指定如何终止该单元的进程。控制组、过程、混合、无之一。如果设置为 control-group,则在 unit stop 时将杀死本单元 control group 中的所有剩余进程(对于服务:执行 stop 命令后,如 ExecStop= 配置的那样)。如果设置为 process,则只会杀死主进程本身。如果设置为混合,则 SIGTERM 信号(见下文)被发送到主进程,而随后的 SIGKILL 信号(见下文)被发送到单元控制组的所有剩余进程。如果设置为 none,则不会杀死任何进程。在这种情况下,单元停止时只会执行停止命令,否则不会杀死任何进程。停止后还活着的进程留在他们的控制组中,控制组在停止后继续存在,除非它是空的。

慕少森

如果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

青春有我

据我所知,解决这个问题的唯一可行方法是使用不同的 cgroup 启动子进程。您可以使用 systemd-run 命令和 --slice 参数来做到这一点。systemd-run --user --scope --slice=app-firefox firefox更改 KillMode 还意味着如果您的主进程崩溃,并且任何子进程仍在运行,systemd 将不会重新启动它。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go