golang exec.Command 导致大量死进程

golang exec.Command cause a lot of defunct processes

我正在使用golang 调用pppd,过一会儿再杀掉它。但是我以这种方式得到了很多失效的进程。

我就是这样 运行 pppd

exec.Command("sh", "-c", "pppd call vpn").CombinedOutput()

我就是这样杀死它的。

exec.Command("sh", "-c", "pkill pppd").CombinedOutput()

然后我得到了很多这个

root     31541 23536  0 10:54 ?        00:00:00 [pppd] <defunct>
root     31929 23356  0 10:55 ?        00:00:00 [pptpgw] <defunct>
root     31933 23356  0 10:55 ?        00:00:00 [pptpcm] <defunct>
root     31940 23356  0 10:55 ?        00:00:00 [pppd] <defunct>
root     31993 23536  0 10:55 ?        00:00:00 [pptpgw] <defunct>
root     31997 23536  0 10:55 ?        00:00:00 [pptpcm] <defunct>
root     31998 23536  0 10:55 ?        00:00:00 [pppd] <defunct>
root     32012 23356  0 10:55 ?        00:00:00 [pptpgw] <defunct>
root     32016 23356  0 10:55 ?        00:00:00 [pptpcm] <defunct>
root     32017 23356  0 10:56 ?        00:00:00 [pppd] <defunct>
root     32070 23536  0 10:56 ?        00:00:00 [pptpgw] <defunct>
root     32074 23536  0 10:56 ?        00:00:00 [pptpcm] <defunct>
root     32075 23536  0 10:56 ?        00:00:00 [pppd] <defunct>
root     32083 23356  0 10:56 ?        00:00:00 [pptpgw] <defunct>
root     32087 23356  0 10:56 ?        00:00:00 [pptpcm] <defunct>
root     32089 23356  0 10:56 ?        00:00:00 [pppd] <defunct>
root     32131 23536  0 10:57 ?        00:00:00 [pptpgw] <defunct>
root     32135 23536  0 10:57 ?        00:00:00 [pptpcm] <defunct>
root     32148 23536  0 10:57 ?        00:00:00 [pppd] <defunct>
root     32160 23356  0 10:57 ?        00:00:00 [pptpgw] <defunct>
root     32164 23356  0 10:57 ?        00:00:00 [pptpcm] <defunct>
root     32165 23356  0 10:57 ?        00:00:00 [pppd] <defunct>
root     32177 23536  0 10:57 ?        00:00:00 [pptpgw] <defunct>
root     32181 23536  0 10:57 ?        00:00:00 [pptpcm] <defunct>

如何避免死进程。

这些 "zombie" processes 是在进程完成时创建的,但父进程尚未通过 wait 系统调用读取它们的退出状态。

我想您需要做的就是对您创建的每个命令结构调用 (*Cmd).Wait()。显然,这可能没有您喜欢的那么直接,因为您可能不想在第二个命令完成之前在第一个命令上调用 Wait


编辑:正如评论中指出的那样,(*Cmd).CombinedOutput() calls (*Cmd).Run() 调用了 (*Cmd).Wait()... 所以上面的内容是错误的。在这种情况下真正的答案是由于某种原因 sh 没有清理,所以解决方案是切断中间人并像这样进行调用:

exec.Command("pppd", "call", "vpn").CombinedOutput()

这将教会我下次更仔细地阅读文档...

取消命令的更简单方法是使用 exec.CommandContext。例如

ctx, cancel := context.WithCancel(context.Background())
exec.CommandContext(ctx, "pppd", "call", "vpn").CombinedOutput()

// in some other goroutine...
cancel()

也许这会解决您的僵尸问题?

运行 新线程中的子进程

go exec.Command("sh", "-c", "pppd call vpn").CombinedOutput()

杀死子进程

exec.Command("pkill", "pppd").CombinedOutput().CombinedOutput()