分享
  1. 首页
  2. 文章

Golang -- Signal处理

logo-fox · · 3028 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

我们在生产环境下运行的系统要求优雅退出,即程序接收退出通知后,会有机会先执行一段清理代码,将收尾工作做完后再真正退出。我们采用系统Signal来 通知系统退出,即kill pragram-pid。我们在程序中针对一些系统信号设置了处理函数,当收到信号后,会执行相关清理程序或通知各个子进程做自清理。kill -9强制杀掉程序是不能被接受的,那样会导致某些处理过程被强制中断,留下无法恢复的现场,导致消息被破坏,影响下次系统启动运行。

最近用Golang实现的一个代理程序也需要优雅退出,因此我尝试了解了一下Golang中对系统Signal的处理方式,这里和大家分享。Golang 的系统信号处理主要涉及os包、os.signal包以及syscall包。其中最主要的函数是signal包中的Notify函数:

func Notify(c chan<- os.Signal, sig ...os.Signal)

该函数会将进程收到的系统Signal转发给channel c。转发哪些信号由该函数的可变参数决定,如果你没有传入sig参数,那么Notify会将系统收到的所有信号转发给c。如果你像下面这样调用Notify:

signal.Notify(c, syscall.SIGINT, syscall.SIGUSR1, syscall.SIGUSR2)

则Go只会关注你传入的Signal类型,其他Signal将会按照默认方式处理,大多都是进程退出。因此你需要在Notify中传入你要关注和处理的Signal类型,也就是拦截它们,提供自定义处理函数来改变它们的行为。

下面是一个较为完整的例子:

//signal.go

package main
import "fmt"
import "time"
import "os"
import "os/signal"
import "syscall"
type signalHandler func(s os.Signal, arg interface{})
type signalSet struct {
 m map[os.Signal]signalHandler
}
func signalSetNew()(*signalSet){
 ss := new(signalSet)
 ss.m = make(map[os.Signal]signalHandler)
 return ss
}
func (set *signalSet) register(s os.Signal, handler signalHandler) {
 if _, found := set.m[s]; !found {
 set.m[s] = handler
 }
}
func (set *signalSet) handle(sig os.Signal, arg interface{})(err error) {
 if _, found := set.m[sig]; found {
 set.m[sig](sig, arg)
 return nil
 } else {
 return fmt.Errorf("No handler available for signal %v", sig)
 }
 panic("won't reach here")
}
func main() {
 go sysSignalHandleDemo()
 time.Sleep(time.Hour) // make the main goroutine wait!
}
func sysSignalHandleDemo() {
 ss := signalSetNew()
 handler := func(s os.Signal, arg interface{}) {
 fmt.Printf("handle signal: %v\n", s)
 }
 ss.register(syscall.SIGINT, handler)
 ss.register(syscall.SIGUSR1, handler)
 ss.register(syscall.SIGUSR2, handler)
 for {
 c := make(chan os.Signal)
 var sigs []os.Signal
 for sig := range ss.m {
 sigs = append(sigs, sig)
 }
 signal.Notify(c)
 sig := <-c
 err := ss.handle(sig, nil)
 if (err != nil) {
 fmt.Printf("unknown signal received: %v\n", sig)
 os.Exit(1)
 }
 }
}

上例中Notify函数只有一个参数,没有传入要关注的sig,因此程序会将收到的所有类型Signal都转发到channel c中。build该源文件并执行程序:

$> go build signal.go
$> signal 

在另外一个窗口下执行如下命令:

$> ps -ef|grep signal
tonybai 25271 1087 0 16:27 pts/1 00:00:00 signal
$> kill -n 2 25271
$> kill -n 12 25271
$> kill 25271

我们在第一个窗口会看到如下输出:

$> signal
handle signal: interrupt
handle signal: user defined signal 2
unknown signal received: terminated

在sysSignalHandleDemo中我们也可以为Notify传入我们所关注的Signal集合:

signal.Notify(c, sigs...)

这样只有在该集合中的信号我们才能捕获,收到未在集合中的信号时,程序多直接退出。上面只是一个Demo,只是说明了我们可以捕捉到我们所关注的信号,并未体现程序如何优雅退出,不同程序的退出方式不同,这里没有通用方法,就不细说了,你的程序需要你专门的设计。

转自:http://tonybai.com/2012/09/21/signal-handling-in-go/


有疑问加站长微信联系(非本文作者)

本文来自:博客园

感谢作者:logo-fox

查看原文:Golang -- Signal处理

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
3028 次点击
被以下专栏收入,发现更多相似内容
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏