Go语言signal包使用指南
go语言学习笔记:signal包
os/signal主要用于实现对信号的处理(官方文档)
信号类型
首先关于linux信号机制课自行查找资料,这里不再赘述。linux中的全部信号如下图:
在go中预定义了几种信号:
//"syscall"
const (
// More invented values for signals
SIGHUP = Signal(0x1)
SIGINT = Signal(0x2)
SIGQUIT = Signal(0x3)
SIGILL = Signal(0x4)
SIGTRAP = Signal(0x5)
SIGABRT = Signal(0x6)
SIGBUS = Signal(0x7)
SIGFPE = Signal(0x8)
SIGKILL = Signal(0x9)
SIGSEGV = Signal(0xb)
SIGPIPE = Signal(0xd)
SIGALRM = Signal(0xe)
SIGTERM = Signal(0xf)
)
首先在这么多信号中,SIGKILL和SIGSTOP是无法被程序捕获的,其中SIGKILL就是我们常用的kill -9 pid方法锁触发的。其次一些有程序执行中的错误所触发的同步信号如SIGBUS,SIGFPE和SIGSEGV,go会将其转为panic,不过若是我们通过kill方式触发也是可以被捕获的。
除了那些同步信号,其余都是异步信号,是由内核或其他程序发送的,我们都可以捕获。
在异步信号中,当程序丢失终端时收到SIGHUP,在终端按下中断字符(一般为ctrl+c)时收到SIGINT,在终端按下退出字符(一般为^\)时收到SIGQUIT。
正常的,信号都是有默认动作的,最常见的如按下ctrl+c退出程序。其余的SIGHUP,SIGINT或SIGTERM信号导致程序退出。SIGQUIT,SIGILL,SIGTRAP,SIGABRT,SIGSTKFLT,SIGEMT或SIGSYS信号导致程序以堆栈转储退出。SIGTSTP,SIGTTIN或SIGTTOU信号获取系统默认行为(shell使用这些信号进行作业控制)。SIGPROF会被go运行时捕获实现runtime.CPUProfile.
捕获信号
signal包中提供了Notify方法,用于注册所要监听的信号。
func Notify(c chan<- os.Signal, sig ...os.Signal)
该方法需要提供一个Signal类型的channel,以及要监听的信号(当不指定时会监听所有信号)。当有信号到来时,会被写入所传入的channel中,之后拿出来即可。下例是一个监听ctrl+c退出的程序:
func main() {
c := make(chan os.Signal, 1)
signal.Notify(c, syscall.SIGINT)
s := <-c
fmt.Println("Got signal:", s)
}
上述程序会在第五行阻塞,直到有一个信号过来。运行结果如下:
其余api
func Stop(c chan<- os.Signal)
这个方法用于停止监听信号,之后不会再往所指定的channel中写入任何内容。搭配Notify使用如下:
go func() {
sigc := make(chan os.Signal, 1)
signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM) //监听信号
defer signal.Stop(sigc) //确保关闭
<-sigc //线程阻塞
log.Info("Got interrupt, shutting down...")
go Stop() //执行程序停止逻辑
}()
func Ignore(sig …os.Signal)
忽略指定的信号,同理,若是未指定,忽略所有信号
func Ignored(sig os.Signal) bool
检查某个信号是否被忽略
func Reset(sig …os.Signal)
重置之前调用Notify时的处理。也就是不在捕获信号,进行默认操作。注意和Ignore的区别,Ignore是不对信号做任何操作,Reset是恢复默认操作。
附录
部分信号说明(图片来源于网络):