You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Jun 26, 2023. It is now read-only.
代码
相关测试用例在
tests/test_kill.go
问题描述
当我们使用
go run tests/test_kill.go
运行测试代码,然后使用
pstree -p
查看进程树可以看到 go 进程
go(30371)─┬─test_kill(30468)─┬─bash(30473)───sleep(30474)
创建了子进程
bash(30473)
,该进程又创建了子shell去执行 shell 命令sleep(30474)
等待5s后,ctx 理应取消,这时查看进程树
可以看到 子进程
bash(30473)
被kill了,但是其子shellsleep(30474)
没有被回收,而是变成了孤儿进程同时我们也有几个协程没有结束,查看pprof 堆栈
/go/src/scheduler/tests/test_kill.go:23
这里就是我们调用exec的地方,可以发现,在os.(*Process).Wait(...) /usr/local/go/src/os/exec.go:129
处发生了阻塞
代码如下
可以看到一直在等待
c.errch
直接看源码,可以发现,在
func (c *Cmd) Start() error {}
处有使用该chan查看源码 可以发现,
c.errch <- fn()
,这个fn
是从c.goroutine
中遍历得到,goroutine []func() error
,根据以上代码,可以发现主要用于处理输入输出,所以可以确定,我们的golang协程是阻塞在此处,用于接受shell的输入输出,
goroutine 通过 创建的
Pipe管道
,在协程中将pr中的数据拷贝到w中.即通过goroutine 读取子进程的输出但是由于我们之前说的子shell,他会拷贝当前shell的状态,也就是会把pipe一起进行拷贝,此时,但又由于我们kill没有kill子shell,
导致我们的pipe的写入端被子shell持有,我们的协程会等待pipe关闭后才返回,导致我们程序无法5s后结束的问题
问题总结
golang 的 exec 在 使用子进程时会和子进程通过pipe建立连接,通过管道获取子进程的输入输出.在某些情况下,子进程会从当前进程fock出子shell(子子进程),
该子子进程进程会拷贝子进程的pipe,当我们在golang kill 子进程 时,如果没有将 子子进程 一起kill,则会导致pipe 的文件描述符被子子进程持有,而无法关闭.
同时我们的 获取子进程的输入输出 的操作会一致等待 pipe关闭后返回,所以出现 协程无法退出的情况
解决办法
golang 中将子进程相关的进程组一起杀死
The text was updated successfully, but these errors were encountered: