exec.Command僵尸进程问题

2021/10/31 实践总结Go

# 问题

exec.Command是Go标准库提供了一个可以很容易地运行外部命令的方法,但是如果使用不当会容易出现僵尸进程的问题。

僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

exec.Command执行的方法有两种,一种是直接调用Run()方法:

cmd := exec.Command("ls")
cmd.Run()
1
2

另外一种是用start方法

cmd := exec.Command("ls")cmd.Start()
cmd.Wait()
1
2

区别在于

  • 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()
}
1
2
3
4
5
6

Wait的功能就是获取子进程的状态,如果不调用就会导致僵尸进程的,可以用一下例子验证:

执行

func main() {
   cmd := exec.Command("ls")
   cmd.Start()
   time.Sleep(100* time.Second)
}
1
2
3
4
5

查询僵尸进程(mac)

✗ ps -A -ostat,ppid,pid,command | grep -e '^[Zz]'
Z+   24625 24626 (ls) // 结果出现一个僵尸进程
1
2

# 结论

cmd.Start()后要记得调用Wait方法,否则会出现僵尸进程