I have a daemon process running and that starts another process. To simulate it right now I have just put 'sleep'. If I kill the process, it remains as zombie. How to clean it properly.
cmd := exec.Command("sleep", "500")
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
if err := cmd.Process.Kill(); err != nil {
log.Fatal("failed to kill process: ", err)
}
time.Sleep(10000000 * time.Millisecond)
$ ps aux | grep sleep
37342 0.0 0.0 4276984 1040 s000 S+ 5:09PM 0:00.00 grep sleep
37309 0.0 0.0 0 0 ?? Z 5:09PM 0:00.00 (sleep)
You need to cmd.Wait()
for it to finish. (In Unix in general, you need to wait(2) to avoid leaking zombies.)
"os/exec"
doesn't have a non-blocking variant of this (there's no equivalent to waitpid(2)) but you can wait in a goroutine:
// Start the subprocess
cmd := exec.Command("sleep", "500")
err := cmd.Start()
if err != nil {
log.Fatal(err)
}
// Wait for it to finish
done := make(chan struct{})
go (func () {
cmd.Wait()
close(done)
})()
// Set a timeout
timeout := time.NewTimer(5 * time.Second)
select {
case <-done:
fmt.Println("process completed")
if !timeout.Stop() {
<-timeout.C
}
case <-timeout.C:
fmt.Println("deadline ran out, killing process")
if err := cmd.Process.Kill(); err != nil {
log.Fatal("failed to kill process: ", err)
}
<-done
}
Only one branch of the select
will fire, and each does the cleanup necessary for the other one. In the timeout case, after the process is killed, Wait()
should return immediately, which should signal the "done" channel.