Linux|如何避免 Go 命令行执行产生“孤儿”进程?

文章图片
文章图片
文章图片
在 Go 程序当中 , 如果我们要执行命令时 , 通常会使用 exec.Command, 也比较好用 , 通常状况下 , 可以达到我们的目的 , 如果我们逻辑当中 , 需要终止这个进程 , 则可以快速使用 cmd.Process.Kill() 方法来结束进程 。 但当我们要执行的命令会启动其他子进程来操作的时候 , 会发生什么情况?
一 孤儿进程的产生 测试小程序:
func kill(cmd *exec.Cmd) func() { return func() { if cmd != nil { cmd.Process.Kill()func main() { cmd := exec.Command(\"/bin/bash\" \"-c\" \"watch toptop.log\") time.AfterFunc(1*time.Second kill(cmd)) err := cmd.Run() fmt.Printf(\"pid=%d err=%s\\" cmd.Process.Pid err) 执行小程序:
go run main.gopid=27326 err=signal: killed 查看进程信息:
ps -jUSER PID PPID PGID SESS JOBC STAT TT TIME COMMANDking 24324 1 24303 0 0 S s012 0:00.01 watch top 可以看到这个 \"watch top\" 的 PPID 为 1 , 说明这个进程已经变成了 “孤儿” 进程 。
那为什么会这样 , 这并不符合我们预期 , 那么可以从 Go 的文档中找到答案:
二 通过进程组来解决掉所有子进程 在 linux 当中 , 是有会话、进程组和进程组的概念 , 并且 Go 也是使用 linux 的 kill(2) 方法来发送信号的 , 那么是否可以通过 kill 来将要结束进程的子进程都结束掉?
linux 的 kill(2) 的定义如下:
并在方法的描述中 , 可以看到如下内容:
如果 pid 为正数的时候 , 会给指定的 pid 发送 sig 信号 , 如果 pid 为负数的时候 , 会给这个进程组发送 sig 信号 , 那么我们可以通过进程组来将所有子进程退出掉?改一下 Go 程序中 kill 方法:
func kill(cmd *exec.Cmd) func() { return func() { if cmd != nil { // cmd.Process.Kill() syscall.Kill(-cmd.Process.Pid syscall.SIGKILL)func main() { cmd := exec.Command(\"/bin/bash\" \"-c\" \"watch toptop.log\") time.AfterFunc(1*time.Second kill(cmd)) err := cmd.Run() fmt.Printf(\"pid=%d err=%s\\" cmd.Process.Pid err) 再次执行:
go run main.go 会发现程序卡住了 , 我们来看一下当前执行的进程:
ps -jUSER PID PPID PGID SESS JOBC STAT TT TIME COMMANDking 27655 91597 27655 0 1 S+ s012 0:01.10 go run main.goking 27672 27655 27655 0 1 S+ s012 0:00.03 ..../exe/mainking 27673 27672 27655 0 1 S+ s012 0:00.00 /bin/bash -c watch toptop.logking 27674 27673 27655 0 1 S+ s012 0:00.01 watch top 可以看到我们 go run 产生了一个子进程 27672(command 那里是 go 执行的临时目录 , 比较长 , 因此添加了省略号) , 27672 产生了 27673(watch toptop.log)进程 , 27673 产生了 27674(watch top)进程 。 那为什么没有将这些子进程都关闭掉呢?
其实之类犯了一个低级错误 , 从上图中 , 我们可以看到他们的进程组 ID 为 27655 , 但是我们传递的是 cmd 的 id 即 27673 , 这个并不是进程组的 ID , 因此程序并没有 kill , 导致 cmd.Run() 一直在执行 。
在 Linux 中 , 进程组中的第一个进程 , 被称为进程组 Leader , 同时这个进程组的 ID 就是这个进程的 ID , 从这个进程中创建的其他进程 , 都会继承这个进程的进程组和会话信息;从上面可以看出 go run main.go 程序 PID 和 PGID 同为 27655 , 那么这个进程就是进程组 Leader , 我们不能 kill 这个进程组 , 除非想“自杀” , 哈哈哈 。
- 创投圈|抖音小店无货源适合新手小白么?如何精细化运营?新手小白看来
- 松下|淘宝店铺信誉分等级如何提升?
- PHP|如何降低用户关注的非必要页面的权重传递?
- 量子纠缠存在于任何维度空间?人类如何逃出三维空间变成“神”?
- 显卡|如何组装旗舰游戏电脑?这里有你想要的答案
- 阿维塔|在避免患上大公司病上,字节跳动、小米保持高度一致
- 火星和地球交换位置会如何?火星会出现生命吗?答案没你想得简单
- 快手视频|视频号和抖音快手的差异化在哪里呢?你应该如何选择适合你的平台
- AirPods|如何进行微信活动运营才有效?
- Linux|无利可图?又一家电子大厂解散团队退出苹果供应链,库克行动了!
