在golang中:防止子进程接收来自调用进程的信号。

huangapple go评论78阅读模式
英文:

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

问题

给定以下代码:

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.

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

英文:

Given is the following code:

package main
import (
&quot;os&quot;
&quot;fmt&quot;
&quot;os/exec&quot;
&quot;os/signal&quot;
&quot;syscall&quot;
)
const NUMBER_OF_PEASANTS = 3
func createPeasants() map[string]*exec.Cmd {
peasants := map[string]*exec.Cmd{}
for i := 0; i &lt; NUMBER_OF_PEASANTS; i++ {
name := fmt.Sprintf(&quot;peasant#%d&quot;, i + 1)
fmt.Printf(&quot;[master] Start %s...\n&quot;, 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 := &lt;-c
fmt.Printf(&quot;[master] Got signal %v but still running...\n&quot;, s)
}
}
func peasantWaitForSignal(name string, c chan os.Signal) {
s := &lt;-c
fmt.Printf(&quot;[%s] Got signal %v and will exit not with code 66\n&quot;, 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(&quot;[master] %s ended with exit code %d.\n&quot;, name, waitStatus.ExitStatus())
} else {
panic(err)
}
}
}
}
func actAsMaster() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
peasants := createPeasants()
fmt.Printf(&quot;[master] Started.\n&quot;)
go masterWaitForSignal(c)
waitForPeasants(peasants)
fmt.Printf(&quot;[master] Done.\n&quot;)
}
func actAsPeasant(name string) {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
fmt.Printf(&quot;[%s] Started.\n&quot;, name)
peasantWaitForSignal(name, c)
fmt.Printf(&quot;[%s] Done.\n&quot;, name)
}
func main() {
if len(os.Args) &gt; 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.

答案1

得分: 11

子进程默认在同一进程组中启动,当你按下 ctrl+c 时,你的 shell 会向所有进程发送信号,试图在中断程序时清理它们。

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

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

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

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 = &amp;syscall.SysProcAttr{
Setpgid: true,
Pgid:    0,
}

huangapple
  • 本文由 发表于 2016年2月16日 21:19:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/35433741.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定