问题
exec.Command是Go标准库提供了一个可以很容易地运行外部命令的方法,但是如果使用不当会容易出现僵尸进程的问题。
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
exec.Command执行的方法有两种,一种是直接调用Run()方法:
cmd := exec.Command("ls")
cmd.Run()
另外一种是用start方法
cmd := exec.Command("ls")cmd.Start()
cmd.Wait()
区别在于
-
Run()方法是在“ls”执行完后才返回
-
Start()方法则是异步的,方法调用完并不是代表结束,需要配合cmd.Wait()
Run和Start方式本质是一样的,查看Run()方法的代码,可以看到实际上也是调用Start后直接调用Wait(),等待Wait返回
func (c *Cmd) Run() error {
if err := c.Start(); err != nil {
return err
}
return c.Wait()
}
Wait的功能就是获取子进程的状态,如果不调用就会导致僵尸进程的,可以用一下例子验证:
执行
func main() {
cmd := exec.Command("ls")
cmd.Start()
time.Sleep(100* time.Second)
}
查询僵尸进程(mac)
✗ ps -A -ostat,ppid,pid,command | grep -e '^[Zz]'
Z+ 24625 24626 (ls) // 结果出现一个僵尸进程
结论
cmd.Start()后要记得调用Wait方法,否则会出现僵尸进程
原文: