【问题标题】:Golang context.WithTimeout doesn't work with exec.CommandContext "su -c" commandGolang context.WithTimeout 不适用于 exec.CommandContext "su -c" 命令
【发布时间】:2021-05-29 10:56:15
【问题描述】:

这段代码正常工作,它打印Timeout:

ctx, cancel := context.WithTimeout(context.Background(), 1000 * time.Millisecond)
process := exec.CommandContext(ctx, "sleep", "5")

processOutBytes, err := process.Output()

if ctx.Err() == context.DeadlineExceeded {
    fmt.Println("Timeout")
}

但是这个,只是另一个命令,不是。进程可以无限工作!

ctx, cancel := context.WithTimeout(context.Background(), 1000 * time.Millisecond)
process := exec.CommandContext(ctx, "su", "-", "myuser", "-c", "python3 main.py")

processOutBytes, err := process.Output()

if ctx.Err() == context.DeadlineExceeded {
    fmt.Println("Timeout")
}

在基于 ubuntu:20.04 的 docker 容器内的 Ubuntu 20.04 上运行。为什么会这样以及如何使第二个代码起作用?

【问题讨论】:

  • 您确定没有返回除 context.DeadlineExceeded 以外的错误吗?
  • 您确定在上下文超时之前该过程没有完成吗?
  • 超时只适用于su进程,下一个调用的进程会继续运行。
  • 彼得,Elias Van Ootegem,是的,当然。例如,如果我在 main.py 中有“while True: pass”,则 process.Output() 将无限工作。

标签: go unix su


【解决方案1】:

超时只适用于exec启动的进程,不会杀死任何子进程。在您的情况下,它将杀死 su 但不会杀死下一个 python3 进程。

要杀死由给定进程启动的所有子进程,您可以在新进程组中启动它并通过发送SIGKILL-pid(负 pid)来杀死整个组,如下所示:

ctx, cancel := context.WithTimeout(context.Background(), 1000 * time.Millisecond)
process := exec.CommandContext(ctx, "su", "-", "myuser", "-c", "python3 main.py")

process.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
go func() {
    <-ctx.Done()
    if ctx.Err() == context.DeadlineExceeded {
        syscall.Kill(-process.Process.Pid, syscall.SIGKILL)
    }
}()

processOutBytes, err := process.Output()
cancel()

if ctx.Err() == context.DeadlineExceeded {
    fmt.Println("Timeout")
}

还要注意,依赖于syscall 的代码是不可移植的;例如,它甚至无法在 Windows 上编译。

【讨论】:

  • 谢谢你的回答,但问题不是杀死子python进程。主要问题是使用此命令 ctx 永远不会完成。例如,尝试在 main.py 中输入“while True: pass” - process.Output() 函数将永远不会返回任何内容。甚至 ctx.Done() 通道也永远不会触发你的进程 kill goroutine。
  • 你试过我的代码了吗? ctx 将在截止日期后完成。
  • 当然我试过了——在我的情况下,ctx 将不会在截止日期之后完成。即使我们使用 &lt;-time.After(1000 * time.Millisecond) 作为杀手 goroutine 的触发器,它不会杀死 processprocess.Output() 仍然可以无限工作。但是,我找到了一个简单的解决方案:将process.Output() 替换为process.Run() 并使用process.StdoutPipe() 手动处理stdout。在这种情况下,上下文可以完成并杀死process。但是您的回答也很有用 - 在我的项目中,我还需要杀死 process 的子进程。
猜你喜欢
  • 2018-07-23
  • 1970-01-01
  • 1970-01-01
  • 2021-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-17
  • 2011-03-28
相关资源
最近更新 更多