防止在Golang中使用exec.Command时Ctrl+C中断执行。

huangapple go评论87阅读模式
英文:

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 (
	&quot;log&quot;
	&quot;os&quot;
	&quot;os/exec&quot;
	&quot;os/signal&quot;
	&quot;syscall&quot;
)

func sleep() {
	log.Println(&quot;Sleep start&quot;)
	cmd := exec.Command(&quot;sleep&quot;, &quot;60&quot;)
	cmd.Run()
	log.Println(&quot;Sleep stop&quot;)
}

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() {
		&lt;-c
		log.Println(&quot;Receved Ctrl + C&quot;)
	}()

	&lt;-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中的SetpgidPgid字段将命令启动在自己的进程组中。

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(&quot;sleep&quot;, &quot;60&quot;)
cmd.SysProcAttr = &amp;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(&quot;Sleep start&quot;)
        cmd := exec.Command(&quot;sleep&quot;, &quot;10&quot;)
        cmd.Run()
        log.Println(&quot;Sleep stop&quot;)
        doneChannel &lt;- true
    }()

    &lt;-doneChannel
}

huangapple
  • 本文由 发表于 2015年10月16日 16:11:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/33165530.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定