當 golang 呼叫的外部 process 因為 timeout 的因素,而被 kill 的時候,如果只用 process id 去 kill ,而這個外部 process 已經有啟動了 child process 的話,就會造成它的 child process 都變成 orphan process 的問題。因為這些 child process 並不會因為 parent process 被 kill 而自動結束。
有考慮上述問題的有效作法,啟動外部的 process 時,要設定一個新的 process session id。如此,讓新增的 process 與 child process, grand child process 都使用新的 process session id 。當 timeout 發生的時候,對 process id 取負值後發送 kill signal 就可以將這些外部的 process 一次全部清除完畢。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
func sessionKill(cmd *exec.Cmd) error { | |
if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil { | |
log.Println("failed to kill processes of the same session: ", err) | |
return err | |
} | |
return nil | |
} | |
// cmdSessionRunWithTimeout runs cmd, and kills the children on timeout | |
func cmdSessionRunWithTimeout(cmd *exec.Cmd, timeout time.Duration) (error, bool) { | |
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true} | |
log.Debugln("show cmd sysProcAttr.Setsid", cmd.SysProcAttr.Setsid) | |
if err := cmd.Start(); err != nil { | |
log.Errorln(cmd.Path, " start fails: ", err) | |
return err, false | |
} | |
done := make(chan error, 1) | |
go func() { done <- cmd.Wait() }() | |
select { | |
case <-time.After(timeout): | |
log.Printf("timeout, process:%s will be killed", cmd.Path) | |
err := sessionKill(cmd) | |
return err, true | |
case err := <-done: | |
return err, false | |
} | |
} |