【转】Go 中如何优雅关闭子进程?
--------------------
有时候,我们可能会遇到开启多进程的场景。因为开启新进程依赖系统调用,所以一般语言都会提供相应的封装。那么 Go 中是怎么做的呢?
如何在 Go 中运行一个外部命令
有时我们会遇到这样的需求,在一个主进程中启动另外一个进程,而在 Go 中可以使用 exec 包的 Cmd 来轻松实现这类需求,示例代码如下:
这段代码的含义是: 使用 nc -u -l 8080 来模拟一个常驻进程,然后通过 Go 的 exec.Cmd 来运行它,并且 Go 代码不退出,运行代码:
$ go run main.go Start child process with pid 32512
输出结果表明我们已经通过 Go 成功调用外部命令,起了一个子进程,其进程号为 32512,我们还可以通过命令 ps -ef | grep 32512 来确认:
UID PID PPID C STIME TTY TIME CMD xxx 32512 32511 0 3:36PM ttys008 0:00.00 -u -l 8080
如何结束子进程
- 首先想到的就是 kill 命令,尝试使用 kill 32512
$ kill 32512 $ ps -ef | grep 32512 UID PID PPID C STIME TTY TIME CMD 2062309935 35904 35903 0 3:36PM ttys008 0:00.00 (nc)
发现 kill 命令并不好用,进程还在,然后换成 kill -9 也同样不起作用。不过该进程已经停止运行了,可以看到监听由 0:00.00 -u -l 8888 变成了 0:00.00 (nc), 不再监听 8888 端口,只是进程资源还没释放而已。
- 使用 Go 代码结束该进程
因为 Go 的 Cmd 内置了 Process.Kill() 函数,我们可以尝试使用它来关闭子进程,修改代码,添加如下内容:
// After five second, kill cmd's process time.Sleep(5 * time.Second) cmd.Process.Kill()
重新运行代码,发现 5 秒过后,该子进程还在。其实调用 cmd.Process.Kill() 和外部使用 kill 命令是一样的,父进程还没有释放资源,所以子进程不能清理完成。
- 使用 cmd.Wait() 完成资源清理,修改后的完整代码如下:
运行代码,可以得到如下结果:
$ go run main.go Start child process with pid 41666 Child process 41666 exit with err: signal: killed
再通过 ps -ef | grep 41666 命令确认子进程 41666 已不存在。
结语
Go 中 exec.Cmd 封装的很好,对于外部命令调用非常方便,但是使用它的时候,需要注意对子进程的资源进行释放,其关键函数就是 cmd.Wait(), 所以用到 cmd 的地方,一定添加 cmd.Wait() 的逻辑。
参考链接:
- https://golang.org/pkg/os/exec/#Cmd.Wait
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
2019-07-23 golang学习资料
2018-07-23 angular 的进一步深入理解
2018-07-23 go 中goroutine 的使用
2017-07-23 php APC Configuration and Usage Tips and Tricks
2017-07-23 golang 的GOPATH设置的问题
2017-07-23 php apc缓存以及与redis的对比