比如代码这样的
func command(command string, writer io.Writer, notify chan struct{}) {
r, w := io.Pipe()
cmd := exec.Command("bash")
cmd.Dir = "/data"
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} # 这个还不兼容 windows
cmd.Stdin = r
stdout, _ := cmd.StdoutPipe()
stderr, _ := cmd.StderrPipe()
defer stdout.Close()
defer stderr.Close()
cmd.Start()
go trans(stdout, writer) // iocopy
go trans(stderr, writer)
go func() {
<-notify
fmt.Println(cmd.Process.Kill())
w.Close()
}()
fmt.Fprint(w, command)
w.Close()
cmd.Wait()
}
如果执行 yarn build
会执行一个 nodejs 的构建,但是当 notify 通知后,bash 进程退出了。nodejs 进程僵尸了。
怎么保证所有由 bash 开启的所有子进程都被关闭(哪怕强制退出)
1
ic3z 231 天前 via iPhone
先找到子进程再关闭。
|
2
withgeneric 231 天前
你都知道拿 group id 了,你应该知道用 syscall.Kill 啊
|
3
dzdh OP @withgeneric 测试的时候貌似不管用。bash 退了然后 yarn 就直接挂到 init 下了还在跑
|
4
withgeneric 231 天前
@dzdh 给 syscall kill 传 group id ,不是 process id
|
5
withgeneric 231 天前
@dzdh man7.org/linux/man-pages/man2/kill.2.html
If pid is less than -1, then sig is sent to every process in the process group whose ID is -pid. 传取反以后的 group id ,读读文档就好 |
6
ysc3839 231 天前 via Android
之前调查过这个问题,不同操作系统情况不同:
POSIX(Linux 除外)似乎没有通用方法。杀进程组的话,子进程可以创建新的进程组来避免。杀进程树的话,子进程可以 fork 两次来脱离进程树。 Linux 可以用 PID namespace 来实现,当一个 PID namespace 里面的“init”进程退出后,其他进程都会被停止。 Windows 可以用 Job Object 实现。 |
7
xhd2015 231 天前 via iPhone
|
8
xhd2015 231 天前 via iPhone
我记得这个问题曾经让我很头疼,原因是 go 进程开启了 bash ,bash 执行 git ,git 又启动了子进程,但是最终产生了大量的 defunct 状态的进程项,也就是说进程已经销毁了,但是 pid 还留在注册表上,导致最终系统无法再新建进程。
最后的解决方法与 go 没有关系,这个问题似乎是 git 的问题,我起了一个定时任务,定期检查 fefunct 的进程项,然后通过调用 wait 强行把它们从进程表中删除。 |
9
rrfeng 231 天前 via Android
为什么要杀子进程?子进程不会正常退出吗?不正常退出要做错误处理吧?硬杀是不是太粗暴了
|
10
guo4224 231 天前 via iPhone
僵尸要 wait
|
11
dzdh OP @rrfeng
这是个 deployer 的场景。假设构建需要 10 分钟,设置超时 5 分钟,那确实需要强制 kill @withgeneric #5 奇怪。今天上班 go run 好了。。之前 nodejs 进程死活杀不掉。。 pgid, _ := syscall.Getpgid(cmd.Process.Pid) if pgid == -1 { return } syscall.Kill(-pgid, syscall.SIGKILL) |
12
ccsexyz 230 天前
死活杀不掉的看下进程是不是进入 D 状态了
|
13
vimiix 230 天前
linux:
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} windows: cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP} 这样应该可以? |
15
uniquecolesmith 230 天前
使用以下代码解决,来源我的项目 go-zoox/watch: https://github.com/go-zoox/watch/blob/master/process/process.go
```go if err := syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL); err != nil { return fmt.Errorf("failed to kill process: %s", err) } ``` 相关: - https://github.com/go-zoox/watch/blob/master/process/process.go - https://medium.com/@felixge/killing-a-child-process-and-all-of-its-children-in-go-54079af94773 |
16
xhd2015 230 天前 via iPhone
不要 kill ,要 wait
|