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

mon*_*now 15 linux process go 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
Run Code Online (Sandbox Code Playgroud)

请注意,在父进程被终止后,子进程仍处于活动状态.但是,如果我像这样从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
Run Code Online (Sandbox Code Playgroud)

......当我杀死主要进程时,孩子也死了:

$ 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
$
Run Code Online (Sandbox Code Playgroud)

我怎样才能让孩子活下来?

在CentOS Linux版本7.1.1503(Core)下运行go版本go1.4.2 linux/amd64.

mon*_*now 30

解决方案是添加

KillMode=process
Run Code Online (Sandbox Code Playgroud)

到服务区.默认值是control-groupsystemd清理所有子进程.

来自man systemd.kill

KillMode =指定如何杀死此单元的进程.控制组,进程,混合,无.

如果设置为control-group,则该单元的控制组中的所有剩余进程将在单元停止时被终止(对于服务:执行stop命令后,如ExecStop =所配置).如果设置为process,则只会杀死主进程本身.如果设置为mixed,SIGTERM信号(见下文)将发送到主进程,而后续SIGKILL信号(见下文)将发送到该单元控制组的所有剩余进程.如果设置为none,则不会终止任何进程.在这种情况下,只有stop命令才会在单元停止时执行,否则不会杀死任何进程.停止后保持活动的进程留在其对照组中,并且控制组在停止后继续存在,除非它是空的.

  • 是否有办法将流程移出群组? (3认同)

Alb*_*bin 7

我知道解决这个问题的唯一方法是使用 --scope 参数启动子进程。

systemd-run --user --scope firefox
Run Code Online (Sandbox Code Playgroud)

这里也提到了 KillMode,但是更改 KillMode 也意味着如果您的主进程崩溃,如果有任何子进程仍在运行,systemd 将不会重新启动它。

  • 谢谢你!这对我有用,并且比接受的答案更好(至少对我来说),因为我不需要更改 systemd KillMode。 (2认同)