golang信号处理及优雅退出

  • Post author:
  • Post category:golang


每个平台的信号定义或许有些不同。下面列出了POSIX中定义的信号。 Linux 使用34-64信号用作实时系统中。 命令man 7 signal提供了官方的信号介绍。


在POSIX.1-1990标准中定义的信号列表


在这里插入图片描述


在SUSv2和POSIX.1-2001标准中的信号列表:


在这里插入图片描述


kill pid 与 kill -9 pid的区别

  • kill pid的作用是向进程号为pid的进程发送SIGTERM(这是kill默认发送的信号),该信号是一个结束进程的信号且可以被应用程序捕获。若应用程序没有捕获并响应该信号的逻辑代码,则该信号的默认动作是kill掉进程。这是终止指定进程的推荐做法。

  • kill -9 pid 则是向进程号为pid的进程发送 SIGKILL(该信号的编号为9),从本文上面的说明可知,SIGKILL既不能被应用程序捕获,也不能被阻塞或忽略,其动作是立即结束指定进程。通俗地说,应用程序根本无法“感知”SIGKILL信号,它在完全无准备的情况下,就被收到SIGKILL信号的操作系统给干掉了,显然,在这种“暴力”情况下,应用程序完全没有释放当前占用资源的机会。事实上,SIGKILL信号是直接发给init进程的,它收到该信号后,负责终止pid指定的进程。在某些情况下(如进程已经hang死,无法响应正常信号),就可以使用 kill -9 来结束进程。



应用程序如何优雅退出?

Linux Server端的应用程序经常会长时间运行,在运行过程中,可能申请了很多系统资源,也可能保存了很多状态,在这些场景下,我们希望进程在退出前,可以释放资源或将当前状态dump到磁盘上或打印一些重要的日志,也就是希望进程优雅退出(exit gracefully)。



Go中的Signal发送和处理


golang中对信号的处理主要使用os/signal包中的两个方法:

  • notify方法用来监听收到的信号
  • stop方法用来取消监听


编写代码

  • 1.监听全部信号
  • 2.监听指定信号
  • 3.优雅退出go守护进程


demo1.go

package main

import (
	"fmt"
	"os"
	"os/signal"
)

// 监听全部信号
func main() {
	c := make(chan os.Signal)
	// 监听所有信号
	signal.Notify(c)
	fmt.Println("启动了程序")
	s := <-c
	fmt.Println("收到信号:", s)
}


demo2.go

package main

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

// 监听全部信号
func main() {
	c := make(chan os.Signal)
	// 监听指定信号
	signal.Notify(c, os.Interrupt, os.Kill, syscall.SIGUSR1, syscall.SIGUSR2)
	fmt.Println("启动了程序")
	s := <-c
	fmt.Println("收到信号:", s)
}


demo3.go

package main

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

// 优雅退出go守护进程
func main() {
	c := make(chan os.Signal)
	// 监听信号
	signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGUSR1, syscall.SIGUSR2)

	go func() {
		for s := range c {
			switch s {
			case syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM:
				fmt.Println("退出:", s)
				ExitFunc()
			case syscall.SIGUSR1:
				fmt.Println("usr1", s)
			case syscall.SIGUSR2:
				fmt.Println("usr2", s)
			default:
				fmt.Println("其他信号:", s)
			}
		}
	}()
	fmt.Println("启动了程序")
	sum := 0
	for {
		sum++
		fmt.Println("休眠了:", sum, "秒")
		time.Sleep(1 * time.Second)
	}
}

func ExitFunc() {
	fmt.Println("开始退出...")
	fmt.Println("执行清理...")
	fmt.Println("结束退出...")
	os.Exit(0)
}

参考资料:

https://gist.github.com/biezhi/74bfe20f9758210c1be18c64e6992a37

https://github.com/facebookarchive/grace



版权声明:本文为Haikuotiankong11111原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。