前言&背景

平时在做一些开发时难免要调一些shell脚本或者外部程序,golang提供了exec包很方便的帮我们解决了这个问题。但是当外部程序或者shell脚本夯死就使得我们自身的程序很不稳定。与此同时,当我们已经感知到程序脚本运行出现问题时,我们可能需要立刻对程序进行杀死的操作,但是当我们很自然的想到cmd.Process.Kill()时,我们又遇上了另外一个问题,因为这个操作并没有将子进程kill掉,所以我们的需求就出来了,那就是:

Kill进程及其子进程

首先给出解决方案,那就是kill掉进程组

给出一个有测试和原理分析的相关链接,我这里也来说下具体用法。

cmd := exec.Command("/bin/sh", "-c", "...........")
// Go会将PGID设置成与PID相同的值
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)

这里来解释一下上面三行

  • 新建一个cmd
  • 设置该cmd成为一个新的进程组
  • 调用系统Kill方法杀死整个进程组

至此就完成了进程及其所有子进程的删除。

延伸

-cmd.Process.Pid-cmd.Process.Pidnil
sync.RWMutexData map[int64](*exec.Cmd)Datasync.RWMutex

那么问题来了:

如何保证我们杀死的一定是一个存在的并且正在运行的程序?

这里也提供了解决方法:

        for {
            if cmd.Process != nil {
                //加锁map、保存PID、解锁map
                break
            }
            time.Sleep(1 * time.Nanosecond)
        }
cmd.Processnil

如下是判断进程是否结束的代码:

        for {
            if cmd.ProcessState != nil {
                //map加锁、删除map中的key、解锁map
                break
            }
            time.Sleep(1 * time.Nanosecond)
        }
cmd.ProcessStatenilnilcmd.ProcessState

就是这样,以较为优雅的方式控制exec程序的生死 :-)