比如代码这样的
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 开启的所有子进程都被关闭(哪怕强制退出)
|      1ic3z      2024-04-07 00:46:52 +08:00 via iPhone 先找到子进程再关闭。 | 
|      2withgeneric      2024-04-07 01:51:43 +08:00 你都知道拿 group id 了,你应该知道用 syscall.Kill 啊 | 
|  |      3dzdh OP @withgeneric 测试的时候貌似不管用。bash 退了然后 yarn 就直接挂到 init 下了还在跑 | 
|      4withgeneric      2024-04-07 03:32:08 +08:00 @dzdh 给 syscall kill 传 group id ,不是 process id | 
|      5withgeneric      2024-04-07 03:34:01 +08:00 @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 ,读读文档就好 | 
|  |      6ysc3839      2024-04-07 03:58:07 +08:00 via Android 之前调查过这个问题,不同操作系统情况不同: POSIX(Linux 除外)似乎没有通用方法。杀进程组的话,子进程可以创建新的进程组来避免。杀进程树的话,子进程可以 fork 两次来脱离进程树。 Linux 可以用 PID namespace 来实现,当一个 PID namespace 里面的“init”进程退出后,其他进程都会被停止。 Windows 可以用 Job Object 实现。 | 
|      7xhd2015      2024-04-07 07:41:52 +08:00 via iPhone | 
|      8xhd2015      2024-04-07 07:45:31 +08:00 via iPhone 我记得这个问题曾经让我很头疼,原因是 go 进程开启了 bash ,bash 执行 git ,git 又启动了子进程,但是最终产生了大量的 defunct 状态的进程项,也就是说进程已经销毁了,但是 pid 还留在注册表上,导致最终系统无法再新建进程。 最后的解决方法与 go 没有关系,这个问题似乎是 git 的问题,我起了一个定时任务,定期检查 fefunct 的进程项,然后通过调用 wait 强行把它们从进程表中删除。 | 
|  |      9rrfeng      2024-04-07 08:55:07 +08:00 via Android 为什么要杀子进程?子进程不会正常退出吗?不正常退出要做错误处理吧?硬杀是不是太粗暴了 | 
|      10guo4224      2024-04-07 09:08:33 +08:00 via iPhone 僵尸要 wait | 
|  |      11dzdh 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) | 
|  |      12ccsexyz      2024-04-07 14:45:07 +08:00 死活杀不掉的看下进程是不是进入 D 状态了 | 
|  |      13vimiix      2024-04-07 16:17:46 +08:00 linux: cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true} windows: cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP} 这样应该可以? | 
|  |      15uniquecolesmith      2024-04-07 16:39:29 +08:00 使用以下代码解决,来源我的项目 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 | 
|      16xhd2015      2024-04-07 22:19:07 +08:00 via iPhone 不要 kill ,要 wait |