首页 >后端开发 >Golang >考虑到潜在的操作系统不一致,如何可靠地超时并终止 Go 中的子进程?

考虑到潜在的操作系统不一致,如何可靠地超时并终止 Go 中的子进程?

DDD
DDD原创
2024-12-02 15:51:11307浏览

How Can I Reliably Timeout and Terminate Child Processes in Go, Considering Potential OS Inconsistencies?

Go 中的进程超时和子进程终止

在 Go 中,超时和终止子进程的能力对于维护系统稳定性至关重要。确保应用程序的响应能力。然而,进程终止可能会出现不一致,如以下代码所示:

func() {
    var output bytes.Buffer
    cmd := exec.Command("Command", args...)
    cmd.Dir = filepath.Dir(srcFile)
    cmd.Stdout, cmd.Stderr = &output, &output
    if err := cmd.Start(); err != nil {
        return err
    }
    defer time.AfterFunc(time.Second*2, func() {
        fmt.Printf("Nobody got time fo that\n")
        if err := cmd.Process.Signal(syscall.SIGKILL); err != nil {
            fmt.Printf("Error:%s\n", err)
        }
        fmt.Printf("It's dead Jim\n")
    }).Stop()
    err := cmd.Wait()
    fmt.Printf("Done waiting\n")
}()

在此代码中,超时机制似乎有问题:虽然它正确打印“It's dead Jim”,但它无法打印“完成等待”并让子进程继续运行。

根本原因:SIGKILL限制

罪魁祸首在于使用 cmd.Process.Signal(syscall.SIGKILL) 来终止进程。在某些情况下,SIGKILL 可能不足以杀死子进程。根本原因是现代操作系统为每个进程维护单独的进程组。当一个组中的一个进程尝试使用 SIGKILL 终止另一个组中的另一个进程时,信号可能无法有效传递。

解决方案:使用负进程组 ID

为了规避此问题,解决方案涉及将负进程组 ID (-pgid) 与 syscall.Kill 结合使用。通过在进程组 ID 前加上减号,信号将发送到整个进程组而不是单个进程。这可确保子进程也被终止:

cmd := exec.Command( some_command )
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
cmd.Start()

pgid, err := syscall.Getpgid(cmd.Process.Pid)
if err == nil {
    syscall.Kill(-pgid, 15)  // note the minus sign
}

cmd.Wait()

注意事项和平台注意事项

需要注意的是,此解决方案可能无法跨所有操作系统移植。操作进程组的能力在不同平台之间有所不同。虽然此解决方案已经过测试并可在 macOS Yosemite 上运行,但其行为在其他平台(例如 Windows 或某些 Linux 版本)上可能会有所不同。

以上是考虑到潜在的操作系统不一致,如何可靠地超时并终止 Go 中的子进程?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn