Go Signal信号处理

简介: Go Signal信号处理

信号(Signal)是Linux, 类Unix和其它POSIX兼容的操作系统中用来进程间通讯的一种方式。对于Linux系统来说,信号就是软中断,用来通知进程发生了异步事件。

当信号发送到某个进程中时,操作系统会中断该进程的正常流程,并进入相应的信号处理函数执行操作,完成后再回到中断的地方继续执行。

有时候我们想在Go程序中处理Signal信号,比如收到SIGTERM信号后优雅的关闭程序,以及 goroutine结束通知等。

Go 语言提供了对信号处理的包(os/signal)。

Go 中对信号的处理主要使用os/signal包中的两个方法:一个是notify方法用来监听收到的信号;一个是 stop方法用来取消监听。

Go信号通知机制可以通过往一个channel中发送os.Signal实现。

信号类型

个平台的信号定义或许有些不同。下面列出了POSIX中定义的信号。
Linux 使用34-64信号用作实时系统中。
命令 man signal 提供了官方的信号介绍。
在POSIX.1-1990标准中定义的信号列表
信号 动作 说明
SIGHUP 1 Term 终端控制进程结束(终端连接断开)
SIGINT 2 Term 用户发送INTR字符(Ctrl+C)触发
SIGQUIT 3 Core 用户发送QUIT字符(Ctrl+/)触发
SIGILL 4 Core 非法指令(程序错误、试图执行数据段、栈溢出等)
SIGABRT 6 Core 调用abort函数触发
SIGFPE 8 Core 算术运行错误(浮点运算错误、除数为零等)
SIGKILL 9 Term 无条件结束程序(不能被捕获、阻塞或忽略)
SIGSEGV 11 Core 无效内存引用(试图访问不属于自己的内存空间、对只读内存空间进行写操作)
SIGPIPE 13 Term 消息管道损坏(FIFO/Socket通信时,管道未打开而进行写操作)
SIGALRM 14 Term 时钟定时信号
SIGTERM 15 Term 结束程序(可以被捕获、阻塞或忽略)
SIGUSR1 30,10,16 Term 用户保留
SIGUSR2 31,12,17 Term 用户保留
SIGCHLD 20,17,18 Ign 子进程结束(由父进程接收)
SIGCONT 19,18,25 Cont 继续执行已经停止的进程(不能被阻塞)
SIGSTOP 17,19,23 Stop 停止进程(不能被捕获、阻塞或忽略)
SIGTSTP 18,20,24 Stop 停止进程(可以被捕获、阻塞或忽略)
SIGTTIN 21,21,26 Stop 后台程序从终端中读取数据时触发
SIGTTOU 22,22,27 Stop 后台程序向终端中写数据时触发
  • 在SUSv2和POSIX.1-2001标准中的信号列表:
信号 动作 说明
SIGTRAP 5 Core Trap指令触发(如断点,在调试器中使用)
SIGBUS 0,7,10 Core 非法地址(内存地址对齐错误)
SIGPOLL Term Pollable event (Sys V). Synonym for SIGIO
SIGPROF 27,27,29 Term 性能时钟信号(包含系统调用时间和进程占用CPU的时间)
SIGSYS 12,31,12 Core 无效的系统调用(SVr4)
SIGURG 16,23,21 Ign 有紧急数据到达Socket(4.2BSD)
SIGVTALRM 26,26,28 Term 虚拟时钟信号(进程占用CPU的时间)(4.2BSD)
SIGXCPU 24,24,30 Core 超过CPU时间资源限制(4.2BSD)
SIGXFSZ 25,25,31 Core 超过文件大小资源限制(4.2BSD)
第1列为信号名;
第2列为对应的信号值,需要注意的是,有些信号名对应着3个信号值,这是因为这些信号值与平台相关,将man手册中对3个信号值的说明摘出如下,the first one is usually valid for alpha and sparc, the middle one for i386, ppc and sh, and the last one for mips.
第3列为操作系统收到信号后的动作,Term表明默认动作为终止进程,Ign表明默认动作为忽略该信号,Core表明默认动作为终止进程同时输出core dump,Stop表明默认动作为停止进程。
第4列为对信号作用的注释性说明,浅显易懂,这里不再赘述。
需要特别说明的是,SIGKILL和SIGSTOP这两个信号既不能被应用程序捕获,也不能被操作系统阻塞或忽略。

Notify 示例

新建一个测试文件 test5.go

package main
import "fmt"
import "os"
import "os/signal"
import "syscall"
func main() {
   // 创建一个os.Signal channel
   sigs := make(chan os.Signal, 1)
   //创建一个bool channel
   done := make(chan bool, 1)
    //注册要接收的信号,syscall.SIGINT:接收ctrl+c ,syscall.SIGTERM:程序退出
    //信号没有信号参数表示接收所有的信号
   signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
     //此goroutine为执行阻塞接收信号。一旦有了它,它就会打印出来。
    //然后通知程序可以完成。
   go func() {
      sig := <-sigs
      fmt.Println(sig)
      done <- true
   }()
   //程序将在此处等待,直到它预期信号(如Goroutine所示)
   //在“done”上发送一个值,然后退出。
   fmt.Println("awaiting signal")
   <-done
   fmt.Println("exiting")
}

执行 go run test5.go 再敲入 ctrl+c,程序会输出

awaiting signal

interrupt exiting

如果用kill pid 结束程序,则输出

awaiting signal Terminated

取消监听 stop

修改代码,增加 signal.Stop(sigs)

package main
import "fmt"
import "os"
import "os/signal"
import "syscall"
func main() {
  // 创建一个os.Signal channel
  sigs := make(chan os.Signal, 1)
  //创建一个bool channel
  done := make(chan bool, 1)
  //注册要接收的信号,syscall.SIGINT:接收ctrl+c ,syscall.SIGTERM:程序退出
  //信号没有信号参数表示接收所有的信号
  signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
  //此goroutine为执行阻塞接收信号。一旦有了它,它就会打印出来。
  //然后通知程序可以完成。
  go func() {
     sig := <-sigs
     fmt.Println(sig)
     done <- true
  }()
  //不允许继续往sigs中存入内容
  signal.Stop(sigs)
  //程序将在此处等待,直到它预期信号(如Goroutine所示)
  //在“done”上发送一个值,然后退出。
  fmt.Println("awaiting signal")
  <-done
  fmt.Println("exiting")
}

执行 go run test5.go 再敲入 ctrl+c,程序输出仅awaiting signal。不会输出signal.Stop(sigs)之后的内容。

Go 优雅重启

如何实现优雅地重启一个Go网络程序呢。主要要解决两个问题:

1、重启进程不需要关闭端口。

2、保持已有的连接请求不中断,让其执行完成或超时。

大概个执行步骤:

  • fork一个继承侦听套接字的新进程。
  • 子进程初始化并开始接受套接字上的连接。
  • 子进程向父进程发信号,通知父进程停止接收连接并终止。

具体实现请阅读文章Graceful Restart in Golang中提供了一种方式。

目录
相关文章
|
Unix Linux Go
Go Signal信号处理
Go Signal信号处理
117 0
|
14天前
|
运维 监控 算法
监控局域网其他电脑:Go 语言迪杰斯特拉算法的高效应用
在信息化时代,监控局域网成为网络管理与安全防护的关键需求。本文探讨了迪杰斯特拉(Dijkstra)算法在监控局域网中的应用,通过计算最短路径优化数据传输和故障检测。文中提供了使用Go语言实现的代码例程,展示了如何高效地进行网络监控,确保局域网的稳定运行和数据安全。迪杰斯特拉算法能减少传输延迟和带宽消耗,及时发现并处理网络故障,适用于复杂网络环境下的管理和维护。
|
15天前
|
编译器 Go
揭秘 Go 语言中空结构体的强大用法
Go 语言中的空结构体 `struct{}` 不包含任何字段,不占用内存空间。它在实际编程中有多种典型用法:1) 结合 map 实现集合(set)类型;2) 与 channel 搭配用于信号通知;3) 申请超大容量的 Slice 和 Array 以节省内存;4) 作为接口实现时明确表示不关注值。此外,需要注意的是,空结构体作为字段时可能会因内存对齐原因占用额外空间。建议将空结构体放在外层结构体的第一个字段以优化内存使用。
|
15天前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
15天前
|
存储 缓存 监控
企业监控软件中 Go 语言哈希表算法的应用研究与分析
在数字化时代,企业监控软件对企业的稳定运营至关重要。哈希表(散列表)作为高效的数据结构,广泛应用于企业监控中,如设备状态管理、数据分类和缓存机制。Go 语言中的 map 实现了哈希表,能快速处理海量监控数据,确保实时准确反映设备状态,提升系统性能,助力企业实现智能化管理。
29 3
|
16天前
|
SQL 安全 Java
阿里双十一背后的Go语言实践:百万QPS网关的设计与实现
解析阿里核心网关如何利用Go协程池、RingBuffer、零拷贝技术支撑亿级流量。 重点分享: ① 如何用gRPC拦截器实现熔断限流; ② Sync.Map在高并发读写中的取舍。
|
17天前
|
存储 算法 安全
基于 Go 语言的公司内网管理软件哈希表算法深度解析与研究
在数字化办公中,公司内网管理软件通过哈希表算法保障信息安全与高效管理。哈希表基于键值对存储和查找,如用户登录验证、设备信息管理和文件权限控制等场景,Go语言实现的哈希表能快速验证用户信息,提升管理效率,确保网络稳定运行。
27 0
|
19天前
|
开发框架 前端开发 Go
eino — 基于go语言的大模型应用开发框架(二)
本文介绍了如何使用Eino框架实现一个基本的LLM(大语言模型)应用。Eino中的`ChatModel`接口提供了与不同大模型服务(如OpenAI、Ollama等)交互的统一方式,支持生成完整响应、流式响应和绑定工具等功能。`Generate`方法用于生成完整的模型响应,`Stream`方法以流式方式返回结果,`BindTools`方法为模型绑定工具。此外,还介绍了通过`Option`模式配置模型参数及模板功能,支持基于前端和用户自定义的角色及Prompt。目前主要聚焦于`ChatModel`的`Generate`方法,后续将继续深入学习。
167 7
|
20天前
|
存储 开发框架 Devops
eino — 基于go语言的大模型应用开发框架(一)
Eino 是一个受开源社区优秀LLM应用开发框架(如LangChain和LlamaIndex)启发的Go语言框架,强调简洁性、可扩展性和可靠性。它提供了易于复用的组件、强大的编排框架、简洁明了的API、最佳实践集合及实用的DevOps工具,支持快速构建和部署LLM应用。Eino不仅兼容多种模型库(如OpenAI、Ollama、Ark),还提供详细的官方文档和活跃的社区支持,便于开发者上手使用。
125 8
|
20天前
|
存储 Go
Go 语言入门指南:切片
Golang中的切片(Slice)是基于数组的动态序列,支持变长操作。它由指针、长度和容量三部分组成,底层引用一个连续的数组片段。切片提供灵活的增减元素功能,语法形式为`[]T`,其中T为元素类型。相比固定长度的数组,切片更常用,允许动态调整大小,并且多个切片可以共享同一底层数组。通过内置的`make`函数可创建指定长度和容量的切片。需要注意的是,切片不能直接比较,只能与`nil`比较,且空切片的长度为0。
Go 语言入门指南:切片