Files
agentic-coding-workflow/internal/infra/proc/proc_unix.go
T
2026-06-20 19:16:44 +08:00

55 lines
1.5 KiB
Go

//go:build !windows
package proc
import (
"errors"
"os/exec"
"syscall"
"time"
)
// unixGroup 用 POSIX 进程组实现整树终止:子进程经 Setpgid 成为新进程组组长
// (pgid == pid),向负 pgid 发信号即覆盖整个进程组的所有子孙。
type unixGroup struct {
pgid int
}
func newGroup() Group { return &unixGroup{} }
func (g *unixGroup) Prepare(cmd *exec.Cmd) {
if cmd.SysProcAttr == nil {
cmd.SysProcAttr = &syscall.SysProcAttr{}
}
cmd.SysProcAttr.Setpgid = true
}
func (g *unixGroup) Adopt(cmd *exec.Cmd) error {
if cmd.Process == nil {
return errors.New("proc: Adopt before Start")
}
// Setpgid 使子进程成为新进程组组长,pgid == pid。
g.pgid = cmd.Process.Pid
return nil
}
func (g *unixGroup) TerminateGroup(done <-chan struct{}, grace time.Duration) {
if g.pgid <= 0 {
return
}
// 向负 pgid 发信号 = 向整个进程组(含所有子孙)发信号。忽略 ESRCH 等错误。
_ = syscall.Kill(-g.pgid, syscall.SIGTERM)
select {
case <-done:
// 组长已被回收(Cmd.Wait 返回),但其子孙(如 trap SIGTERM 的
// vite/esbuild/node)可能仍存活于同一进程组。无条件向整组补发一次
// SIGKILL 以确保不留下任何孤儿;若组已全灭,内核返回 ESRCH,无害。
_ = syscall.Kill(-g.pgid, syscall.SIGKILL)
case <-time.After(grace):
// 组长自身超过 grace 仍未退出:直接对整组 SIGKILL。
_ = syscall.Kill(-g.pgid, syscall.SIGKILL)
}
}
func (g *unixGroup) Close() {}