2015-10-16 282 views
2

我注意到即使通過signal.Notify截獲了中斷調用,也會中斷從exec.Command開始的進程。我已經做了以下的例子來說明這個問題:防止Ctrl + C中斷Golang中的exec.Command

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命令。

任何想法如何防止這種情況發生?

回答

5

當您按ctrl+c時,外殼將發信號通知整個過程組。如果您直接發信號通知父進程,子進程將不會收到該信號。

爲了防止外殼信令的孩子,你需要在自己的進程組,啓動命令與SetpgidPgid字段syscall.SysProcAttr啓動過程

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

這對windows不起作用,因爲在windows下'SysProcAttr'中沒有'Setpgid'。任何替代品? – tkausl

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 
}