英文:
Prevent Ctrl+C from interrupting exec.Command in Golang
问题
我注意到使用exec.Command
启动的进程即使通过signal.Notify
拦截了中断调用,也会被中断。我做了以下示例来展示这个问题:
package main
import (
"log"
"os"
"os/exec"
"os/signal"
"syscall"
)
func sleep() {
log.Println("Sleep start")
cmd := exec.Command("sleep", "60")
cmd.Run()
log.Println("Sleep stop")
}
func main() {
var doneChannel = make(chan bool)
go sleep()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
signal.Notify(c, syscall.SIGTERM)
go func() {
<-c
log.Println("Receved Ctrl + C")
}()
<-doneChannel
}
如果在程序运行时按下Ctrl+C,它将打印:
2015/10/16 10:05:50 Sleep start
^C2015/10/16 10:05:52 Receved Ctrl + C
2015/10/16 10:05:52 Sleep stop
显示sleep
命令被中断了。虽然成功捕获了Ctrl+C,主程序没有退出,但sleep
命令受到了影响。
有什么办法可以防止这种情况发生吗?
英文:
I've noticed that processes started with exec.Command
get interrupted even when the interrupt call has been intercepted via signal.Notify
. I've done the following example to show the issue:
package main
import (
"log"
"os"
"os/exec"
"os/signal"
"syscall"
)
func sleep() {
log.Println("Sleep start")
cmd := exec.Command("sleep", "60")
cmd.Run()
log.Println("Sleep stop")
}
func main() {
var doneChannel = make(chan bool)
go sleep()
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
signal.Notify(c, syscall.SIGTERM)
go func() {
<-c
log.Println("Receved Ctrl + C")
}()
<-doneChannel
}
If Ctrl+C is pressed while this program is running, it's going to print:
2015/10/16 10:05:50 Sleep start
^C2015/10/16 10:05:52 Receved Ctrl + C
2015/10/16 10:05:52 Sleep stop
showing that the sleep
commands gets interrupted. Ctrl+C is successfully caught though and the main program doesn't quit, it's just the sleep
commands that gets affected.
Any idea how to prevent this from happening?
答案1
得分: 15
当你按下ctrl+c
时,shell会向整个进程组发送信号。如果你直接向父进程发送信号,子进程将不会接收到信号。
为了防止shell向子进程发送信号,你需要在启动进程之前,使用syscall.SysProcAttr
中的Setpgid
和Pgid
字段将命令启动在自己的进程组中。
cmd := exec.Command("sleep", "60")
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
英文:
The shell will signal the entire process group when you press ctrl+c
. If you signal the parent process directly, the child process won't receive the signal.
To prevent the shell from signaling the children, you need to start the command in its own process group with with the Setpgid
and Pgid
fields in syscall.SysProcAttr
before starting the processes
cmd := exec.Command("sleep", "60")
cmd.SysProcAttr = &syscall.SysProcAttr{
Setpgid: true,
}
答案2
得分: -2
你可以忽略syscall.SIGINT
信号,这样它就不会传递给exec.Command
。
func main() {
var doneChannel = make(chan bool)
signal.Ignore(syscall.SIGINT)
go func() {
log.Println("Sleep start")
cmd := exec.Command("sleep", "10")
cmd.Run()
log.Println("Sleep stop")
doneChannel <- true
}()
<-doneChannel
}
英文:
You can ignore the syscall.SIGINT
signal, then it won't be passed to the exec.Command
.
func main() {
var doneChannel = make(chan bool)
signal.Ignore(syscall.SIGINT)
go func() {
log.Println("Sleep start")
cmd := exec.Command("sleep", "10")
cmd.Run()
log.Println("Sleep stop")
doneChannel <- true
}()
<-doneChannel
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论