Go:gsignal,信号的掌控者

Go语言精选

共 1637字,需浏览 4分钟

 ·

2020-12-17 02:15

点击上方蓝色“Go语言中文网”关注,每天一起学 Go

Illustration created for “A Journey With Go”, made from the original Go Gopher, created by Renee French.

ℹ️ 本文基于 Go 1.13。

signal 包提供了信号处理器,让我们的 Go 程序可以与发送来的信号进行交互。在进入内部细节之前,我们先来了解下 listener。

订阅

对信号的订阅是通过通道实现的。下面是一个监听所有中断信号和终端大小改变信号的程序的例子:

每个 os.Signal 通道监听各自对应的事件。下面是前面例子订阅工作流的示意图:

Go 也赋予了通道停止被通知的能力(Stop(os.Signal) 函数)和忽略信号的能力(Ignore(...os.Signal) 函数)。下面是两个函数的例子:

这个程序无法被 CTRL + C 中断,并且永远不会停止,因为在第二次从该通道接收信号之前,该通道已停止侦听终端调整大小的信号。现在我们来看下处理发送来的信号的 listenerprocess 两个阶段是如何内建的。

gsignal

在初始化阶段,signal 开启了一个在循环中运行的协程,作为处理信号的消费者。在循环被通知激活之前,一直处于睡眠状态。下面是第一步的示意图:

然后,当一个信号到达程序时,信号处理器把它代理到一个名为 gsignal 的专用协程。这个协程是用比较大的栈空间(32 K,目的是满足不同操作系统的不同需求)创建的,并且空间固定不能增长。每个线程(图片的 M)内部都有 gsignal 协程来处理信号。下面是更新后的示意图:

gsignal 分析信号,检查信号是否可处理,在把信号发送到队列里时唤醒处于睡眠状态的协程。

SIGBUSSIGFPE 等同步信号不能被管理,会被转为 panic

然后,循环中的协程可以处理它。它首先找到订阅了这个事件的通道,再把信号推进通道:

通过工具 go tool trace 可以可视化对循环处理信号的协程的追踪过程。

gsignal 的阻塞或锁会让信号处理陷入麻烦。由于它的栈空间固定,因此不能再申请内存。这就是在信号处理链中有两个独立的协程很重要的原因:一个协程用于在信号到达时尽快排列信号,另一个协程用循环处理该队列中的信号。

现在我们可以用新的组件来更新第一节的插图了:


via: https://medium.com/a-journey-with-go/go-gsignal-master-of-signals-329f7ff39391

作者:Vincent Blanchon[1]译者:lxbwolf[2]校对:polaris1119[3]

本文由 GCTT[4] 原创编译,Go 中文网[5] 荣誉推出

参考资料

[1]

Vincent Blanchon: https://medium.com/@blanchon.vincent

[2]

lxbwolf: https://github.com/lxbwolf

[3]

polaris1119: https://github.com/polaris1119

[4]

GCTT: https://github.com/studygolang/GCTT

[5]

Go 中文网: https://studygolang.com/



推荐阅读


福利

我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。

浏览 31
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报