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

时间:2015-08-25 16:02:08

标签: linux go process 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 7.1.1503版(核心版)下运行go版本go1.4.2 linux / amd64。

2 个答案:

答案 0 :(得分:25)

解决方案是添加

KillMode=process

到服务区。默认值为control-group,这意味着systemd会清除所有子进程。

来自man systemd.kill

  

KillMode =指定如何杀死此单位的进程。之一   control-group,process,mixed,none。

     

如果设置为control-group,则控制组中的所有剩余进程   这个单位将在单位站点被杀死(服务:停止后   执行命令,如ExecStop =)所配置。如果设置为处理,   只有主要进程本身被杀死。如果设置为mixed,则为SIGTERM   信号(见下文)随后发送到主进程   SIGKILL信号(见下文)被发送到所有剩余的进程   单位的控制组。如果设置为none,则不会终止任何进程。在这   case,只有stop命令才会在单元停止时执行,但是没有   否则进程被杀死。停止后剩余的进程是   留在对照组中,对照组继续存在   停止后,除非它是空的。

答案 1 :(得分:0)

如果由于某种原因您不能(像我一样)更改服务的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