防止Ctrl+C打断exec.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命令受到影响。

你知道如何防止这种情况发生吗?

当您按下ctrl+c时,shell将向整个进程组发出信号。如果你直接给父进程发信号,子进程将不会接收到这个信号。

为了防止shell向子进程发送信号,您需要在启动进程

之前在其自己的进程组中启动该命令,并在syscall.SysProcAttr中使用SetpgidPgid字段。
cmd := exec.Command("sleep", "60")
cmd.SysProcAttr = &syscall.SysProcAttr{
    Setpgid: true,
}

您可以忽略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
}

最新更新