英文:
Golang context.WithTimeout doesn't work with exec.CommandContext "su -c" command
问题
这段代码可以正常工作,它会打印出"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。为什么会这样,如何使第二段代码正常工作?
英文:
This code works properly, it prints 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")
}
But this, with just another command, is not. Process can work infinitely!
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")
}
Running on Ubuntu 20.04 inside docker container based on ubuntu:20.04. Why is this so and how to make the second code work?
答案1
得分: 6
超时只适用于由exec
启动的进程,它不会终止任何子进程。在你的情况下,它会终止su
进程,但不会终止接下来的python3
进程。
要终止由给定进程启动的所有子进程,你可以在一个新的进程组中启动它,并通过向-pid
(负pid)发送SIGKILL
信号来终止整个进程组,代码如下:
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上甚至无法编译。
英文:
The timeout only applies to the process started by exec
, it won't kill any child processes. In your case it will kill the su
but not the next python3
process.
To kill all children started by a given process you can start it in a new process group and kill the entire group by sending SIGKILL
to -pid
(negative pid), like so:
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")
}
Note also that the code relying on syscall
isn't portable; it won't even compile on Windows, for example.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论