简体   繁体   中英

In golang: Prevent child processes to receive signals from calling process

Given is the following code:

package main
import (
    "os"
    "fmt"
    "os/exec"
    "os/signal"
    "syscall"
)

const NUMBER_OF_PEASANTS = 3

func createPeasants() map[string]*exec.Cmd {
    peasants := map[string]*exec.Cmd{}
    for i := 0; i < NUMBER_OF_PEASANTS; i++ {
        name := fmt.Sprintf("peasant#%d", i + 1)
        fmt.Printf("[master] Start %s...\n", name)
        cmd := exec.Command(os.Args[0], name)
        cmd.Stderr = os.Stderr
        cmd.Stdin = os.Stdin
        cmd.Stdout = os.Stdout
        if err := cmd.Start(); err != nil {
            panic(err)
        }
        peasants[name] = cmd
    }
    return peasants
}

func masterWaitForSignal(c chan os.Signal) {
    for true {
        s := <-c
        fmt.Printf("[master] Got signal %v but still running...\n", s)
    }
}

func peasantWaitForSignal(name string, c chan os.Signal) {
    s := <-c
    fmt.Printf("[%s] Got signal %v and will exit not with code 66\n", name, s)
    os.Exit(66)
}

func waitForPeasants(peasants map[string]*exec.Cmd) {
    for name, peasant := range peasants {
        if err := peasant.Wait(); err != nil {
            if exitError, ok := err.(*exec.ExitError); ok {
                waitStatus := exitError.Sys().(syscall.WaitStatus)
                fmt.Printf("[master] %s ended with exit code %d.\n", name, waitStatus.ExitStatus())
            } else {
                panic(err)
            }
        }
    }
}

func actAsMaster() {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)

    peasants := createPeasants()

    fmt.Printf("[master] Started.\n")
    go masterWaitForSignal(c)
    waitForPeasants(peasants)
    fmt.Printf("[master] Done.\n")
}

func actAsPeasant(name string) {
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)

    fmt.Printf("[%s] Started.\n", name)
    peasantWaitForSignal(name, c)
    fmt.Printf("[%s] Done.\n", name)
}

func main() {
    if len(os.Args) > 1 {
        actAsPeasant(os.Args[1])
    } else {
        actAsMaster()
    }
}

When I run the code and hit [Ctrl]+[C] (while it runs) the following output it produced:

$ go run signaldemo.go 
[master] Start peasant#1...
[master] Start peasant#2...
[master] Start peasant#3...
[peasant#1] Started.
[master] Started.
[peasant#2] Started.
[peasant#3] Started.
^C[peasant#2] Got signal interrupt and will exit not with code 66
[peasant#1] Got signal interrupt and will exit not with code 66
[master] Got signal interrupt but still running...
[master] peasant#1 ended with exit code 66.
[master] peasant#2 ended with exit code 66.
[peasant#3] Got signal interrupt and will exit not with code 66
[master] peasant#3 ended with exit code 66.
[master] Done.

How can I prevent that the children receives the Interrupt signal? But I do not want to rewrite the children. A change in the calling process is required.

The child processes are starting in the same process group by default, and your shell is sending the signal to all the processes at once when you press ctrl+c . This is the default behavior of the shell to try and clean up when you interrupt a program.

The child processes won't receive signals sent directly to the parent.

If you want to prevent the ctrl+c behavior, you can force the child processes to start in their own process groups with with the Setpgid and Pgid fields in syscall.SysProcAttr before starting the processes.

cmd.SysProcAttr = &syscall.SysProcAttr{
    Setpgid: true,
    Pgid:    0,
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM