【问题标题】:In golang: Prevent child processes to receive signals from calling process在golang中:防止子进程接收来自调用进程的信号
【发布时间】:2016-05-27 19:14:47
【问题描述】:

给出以下代码:

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()
    }
}

当我运行代码并点击 [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.

如何防止孩子收到中断信号?但我不想重写孩子们。需要更改调用过程。

【问题讨论】:

    标签: go process signals fork


    【解决方案1】:

    默认情况下,子进程在同一个进程组中启动,当您按下ctrl+c 时,您的 shell 会立即向所有进程发送信号。这是在您中断程序时尝试清理的 shell 的默认行为。

    子进程不会接收直接发送给父进程的信号。

    如果您想阻止ctrl+c 行为,您可以在启动进程之前使用syscall.SysProcAttr 中的SetpgidPgid 字段强制子进程在它们自己的进程组中启动。

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

    【讨论】:

    • 这对 Linux 非常有效。我可以期望以下几行对 Windows 执行相同的操作吗? cmd.SysProcAttr = &syscall.SysProcAttr{ CreationFlags: 0x00000008, } 遵循以下代码:msdn.microsoft.com/en-us/library/windows/desktop/…
    • @GreNodge:我不了解 Windows,但这听起来不像是一回事(尽管它可能会做你想做的事)。 syscall.CREATE_NEW_PROCESS_GROUP 的描述听起来类似于 POSIX setpgid。
    • 我都试了。两者似乎都按预期工作。但我同意你的观点, syscall.CREATE_NEW_PROCESS_GROUP 似乎是更好的匹配类型。感谢您的支持。
    • 请注意,此选项将使 docker-compose 进程挂起...仍在调查中,但删除 attrs 有助于修复它
    猜你喜欢
    • 2021-04-15
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-03-22
    • 2020-02-27
    相关资源
    最近更新 更多