简体   繁体   English

在golang中:防止子进程从调用进程接收信号

[英]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: 当我运行代码并按[Ctrl] + [C](运行时)时,它产生以下输出:

$ 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 . 默认情况下,子进程在同一进程组中启动,当您按ctrl+c时,shell会立即将信号发送到所有进程。 This is the default behavior of the shell to try and clean up when you interrupt a program. 这是shell在中断程序时尝试清理的默认行为。

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. 如果要阻止ctrl+c行为,可以在启动进程之前强制子进程在syscall.SysProcAttr使用SetpgidPgid字段在其自己的进程组中启动。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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