Skip to content

Goのos/exec.Commandで実行したプロセスの子孫もまとめて終了する(Unixの場合)

Goの os/exec を使うと、何もしなければ子プロセスが孤児になる。例えば以下のコマンドを実行して、そのプロセスを kill(1) したとき、 exec.Command は終了するが sleep(1) は孤児プロセスになり実行し続ける。

prog/main.go
func main() {
err := exec.Command("sleep", "100").Run()
}

子プロセスも含めて確実に停止するには、プロセスグループへシグナルを送る。このとき、デフォルトでは親プロセスと同じグループを持つので、prog/main.gosleep(1) と、さらに kill(2) を送信する自分自身がすべて同じプロセスグループを持つ。それでは困るため、setpgid(2) を使って(kill(1) のために)新しいプロセスグループを作成する。

prog/main.go
c.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}

これで、Unixの仕様により、prog/main.go と子孫のプロセスが負のプロセスIDを持つことになるため、終了したいときにマイナスを付けて kill(2) すればいい。

err := syscall.Kill(-c.Process.Pid, s)

ライブラリとしてはwallet/internal/procgroupで作った。