防止Ctrl + C在Golang

时间:2015-10-16 08:11:42

标签: multithreading go subprocess goroutine sigint

我注意到,即使通过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命令影响。

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

2 个答案:

答案 0 :(得分:6)

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

要防止shell向子节点发送信号,您需要在开始进程之前使用syscall.SysProcAttr中的SetpgidPgid字段在其自己的进程组中启动该命令

cmd := exec.Command("sleep", "60")
cmd.SysProcAttr = &syscall.SysProcAttr{
    Setpgid: true,
}

答案 1 :(得分:1)

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