Go信号处理

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

Go signal handling

问题

我对Go还不太熟悉,但是我可以帮你翻译一下你的问题。

我对Go还不太熟悉,而且在处理需要返回到主循环的事件时遇到了一些困难。

在C语言中,我可以直接返回函数,但是在Go中好像不能这样做,至少不能用这种方法。

我可以处理需要在sigHandler()函数中处理的信号,比如SIGINT或SIGTERM,但是当处理SIGHUP或SIGUSR时,我需要将调用返回到主函数。在我下面的代码中,一旦我发送了挂起信号,进程就会陷入等待模式。

如果有人能帮助我指导如何正确设计信号处理程序,以处理需要返回到第一个goroutine中的调用,我将不胜感激。

编辑

我现在在主goroutine中使用select{}来处理通道消息,但是当我发送HUP信号时,下面的代码会退出。这里的目标是重新加载配置并正常继续执行。

在主线代码中,我有这样的代码:

go sigHandler()
cs := make(chan bool, 1)
go sigHandler(cs)

// 设置HTTP服务器
err := setupServer(addr, port)
if err != nil {
	fatal("设置监听套接字时出错")
	os.Exit(1)
}

    select {
    case quit := <-cs:
        if quit {
	        logEvent(loginfo, sys, "终止..")
	        closeLog()
	        os.Exit(0)
        } else {
	        logEvent(loginfo, sys, "重新加载配置..")
            }
    }

函数sigHandler()

func sigHandler(cs chan bool) {
    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

    signal := <-c
    logEvent(lognotice, sys, "收到信号: "+signal.String())

    switch signal {
    case syscall.SIGINT, syscall.SIGTERM:
        cs <- true
    case syscall.SIGHUP:
            cs <- false
    }
}
英文:

I'm quite new to Go and in a quite a rut trying to figure out how to handle events that need to return back to the main loop.

In C, I can just return the function back but it seems I can't do that here, at least with that method.

I can handle signals that need to be processed within sigHandler() i.e. SIGINT or SIGTERM but I need to return calls back to main when handling SIGHUP or SIGUSR. In my code below, the process just stuck in wait mode once I sent the hangup signal.

I appreciate if someone can help me to guide on how I can properly design the signal handling to handle calls that need to return to main code which is in the first goroutine.

Edit

I'm now handling channel messages in the main goroutine within select{} but the code below quits when I'm sending the HUP signal. The objective here is to reload the config and continue execution as normal.

In the main line code, I have this:

go sigHandler()
cs := make(chan bool, 1)
go sigHandler(cs)

// setup the http server
err := setupServer(addr, port)
if err != nil {
	fatal(&quot;Error setting up listening sockets&quot;)
	os.Exit(1)
}

    select {
    case quit := &lt;-cs:
        if quit {
	        logEvent(loginfo, sys, &quot;Terminating..&quot;)
	        closeLog()
	        os.Exit(0)
        } else {
	        logEvent(loginfo, sys, &quot;Reloading configuration..&quot;)
            }
    }

The function sigHandler()

func sigHandler(cs chan bool) {
    c := make(chan os.Signal, 1)
    signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

    signal := &lt;-c
    logEvent(lognotice, sys, &quot;Signal received: &quot;+signal.String())

    switch signal {
    case syscall.SIGINT, syscall.SIGTERM:
        cs &lt;- true
    case syscall.SIGHUP:
            cs &lt;- false
    }
}

答案1

得分: 2

你可以这样做:

package main

import (
	"os"
	"os/signal"
	"syscall"
)

// 我们让 sigHandler 接收一个通道,用于报告变量 quit 的值
func sigHandler(q chan bool) {
	var quit bool

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// 对于接收到的每个信号
	for signal := range c {
		// logEvent(lognotice, sys, "Signal received: "+signal.String())

		switch signal {
		case syscall.SIGINT, syscall.SIGTERM:
			quit = true
		case syscall.SIGHUP:
			quit = false
		}

		if quit {
			quit = false
			// closeDb()
			// logEvent(loginfo, sys, "Terminating..")
			// closeLog()
			os.Exit(0)
		}
		// 通过通道报告 quit 的值
		q <- quit
	}
}

func main() {
	// 初始化两个通道,一个用于信号,一个用于主循环
	sig := make(chan bool)
	loop := make(chan error)

	// 启动信号监控例程
	go sigHandler(sig)

	// 只要 quit 为 false,就一直执行循环
	for quit := false; !quit; {
		// 在 goroutine 中启动主循环代码
		go func() {
			// 在这里编写主循环代码
			// 我们可以通过通道报告错误(这里是 nil)
			loop <- nil
		}()

		// 阻塞,直到接收到信号或主代码完成
		select {
		// 如果接收到信号,我们将赋值给 quit 并继续循环
		case quit = <-sig:
		// 如果没有接收到信号,我们只是继续循环
		case <-loop:
		}
	}
}

但是,请注意,信号会导致主循环继续执行,但不会停止第一个 goroutine 的执行。

英文:

You could do something like this:

package main

import (
	&quot;os&quot;
	&quot;os/signal&quot;
	&quot;syscall&quot;
)

// We make sigHandler receive a channel on which we will report the value of var quit
func sigHandler(q chan bool) {
	var quit bool

	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	// foreach signal received
	for signal := range c {
		// logEvent(lognotice, sys, &quot;Signal received: &quot;+signal.String())

		switch signal {
		case syscall.SIGINT, syscall.SIGTERM:
			quit = true
		case syscall.SIGHUP:
			quit = false
		}

		if quit {
			quit = false
			// closeDb()
			// logEvent(loginfo, sys, &quot;Terminating..&quot;)
			// closeLog()
			os.Exit(0)
		}
		// report the value of quit via the channel
		q &lt;- quit
	}
}

func main() {
	// init two channels, one for the signals, one for the main loop
	sig := make(chan bool)
	loop := make(chan error)

	// start the signal monitoring routine
	go sigHandler(sig)

	// while vat quit is false, we keep going
	for quit := false; !quit; {
		// we start the main loop code in a goroutine
		go func() {
			// Main loop code here
			// we can report the error via the chan (here, nil)
			loop &lt;- nil
		}()

		// We block until either a signal is received or the main code finished
		select {
		// if signal, we affect quit and continue with the loop
		case quit = &lt;-sig:
		// if no signal, we simply continue with the loop
		case &lt;-loop:
		}
	}
}

However, note that the signal will cause the main loop to continue, but it will not stop the execution on the first goroutine.

答案2

得分: 2

这是一种实现你想要的功能的结构化方式,将信号处理代码和主要代码分离,使它们可以独立进行测试。

关于如何实现Quit和ReloadConfig完全取决于你的程序-ReloadConfig可以向正在运行的goroutine的通道发送一个“请重新加载”的值;它可以锁定互斥锁并更改一些共享配置数据;或者其他可能性。

package main

import (
	"log"
	"os"
	"os/signal"
	"syscall"
)

func main() {
	obj := &myObject{}
	go handleSignals(obj)
	select {}
}

type myObject struct {
}

func (obj *myObject) Quit() {
	log.Printf("quitting")
	os.Exit(0)
}

func (obj *myObject) ReloadConfig() {
	log.Printf("reloading configuration")
}

type MainObject interface {
	ReloadConfig()
	Quit()
}

func handleSignals(main MainObject) {
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
	for sig := range c {
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			main.Quit()
			return
		case syscall.SIGHUP:
			main.ReloadConfig()
		}
	}
}

希望对你有帮助!

英文:

Here's one way of structuring things to do what you want,
separating concerns so that the signal-handling code
and the main code are separate and easily tested independently.

How you implement Quit and ReloadConfig is entirely up
to your program - ReloadConfig may send a "please reload"
value on a channel to a running goroutine; it may lock
a mutex and change some shared configuration data; or some
other possibility.

package main
import (
&quot;log&quot;
&quot;os&quot;
&quot;os/signal&quot;
&quot;syscall&quot;
)
func main() {
obj := &amp;myObject{}
go handleSignals(obj)
select {}
}
type myObject struct {
}
func (obj *myObject) Quit() {
log.Printf(&quot;quitting&quot;)
os.Exit(0)
}
func (obj *myObject) ReloadConfig() {
log.Printf(&quot;reloading configuration&quot;)
}
type MainObject interface {
ReloadConfig()
Quit()
}
func handleSignals(main MainObject) {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
for sig := range c {
switch sig {
case syscall.SIGINT, syscall.SIGTERM:
main.Quit()
return
case syscall.SIGHUP:
main.ReloadConfig()
}
}
}

huangapple
  • 本文由 发表于 2013年9月20日 12:14:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/18908698.html
匿名

发表评论

匿名网友

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

确定