【发布时间】:2015-11-19 10:33:16
【问题描述】:
我想生成长时间运行的子进程,这些子进程在主进程重新启动/死亡时仍然存在。从终端运行时效果很好:
$ 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。
【问题讨论】:
-
SystemD 可能会杀死进程组(或 cgroup,我有一段时间没有查看 systemd 设置)中的所有内容。您实际上是在尝试模拟守护程序的行为,还是仅作为示例?
-
@JimB:我的实际代码产生了运行 ffmpeg 的子进程。我基本上是在使用上面的模式来模拟守护进程的行为,是的。
-
为什么不让父进程运行,让systemd正常管理呢?我认为可能有一种方法可以生成子进程并通知系统新进程,但前者会容易得多。
-
@JimB:子进程重启成本很高,所以当主进程重启或崩溃时,它们不能死掉。但是,感谢您的 cgroup 提示,我发现解决方案是说
KillMode=process(默认为control-group)。