Linux信号与golang中的捕获处理
什么是信号
在计算机科学中,信号是Unix、类Unix以及其他POSIX兼容的操作系统中进程间通讯的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。
当一个信号发送给一个进程,操作系统中断了进程正常的控制流程,如果进程定义了对信号的处理,此时,程序将进入捕获到的信号对应的处理函数,否则执行默认的处理函数。
Linux中信号的介绍
在Linux系统共定义了64种信号,分为两大类:实时信号与非实时信号,1-31为非实时,32-64种为实时信号。
- 非实时信号: 也称为不可靠信号,为早期Linux所支持的信号,不支持排队,信号可能会丢失, 比如发送多次相同的信号, 进程只能收到一次. 信号值取值区间为1~31;
- 实时信号: 也称为可靠信号,支持排队, 信号不会丢失, 发多少次, 就可以收到多少次. 信号值取值区间为32~64
Linux操作系统中,在终端上执行 kill -l 便可看到系统定义的所有信号

信号表
POSIX.1-1990标准信号
此表参考自:POSIX信号
| 信号 | 值 | 动作 | 说明 |
|---|---|---|---|
| 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 | 后台程序向终端中写数据时触发 |
更多的信号说明请查阅man7
此表的操作为每个信号的默认配置,如下所示
| 动作 | 说明 |
|---|---|
| Term | 默认操作是,终止进程。 |
| Ign | 默认操作是,忽略信号。 |
| Core | 默认操作是,终止该进程并核心转储 |
| Stop | 默认操作是,停止进程。 |
| Cont | 默认操作是,如果当前已停止,则继续该进程。 |
信号的产生
信号是事件发生时对进程的通知机制。信号中断与硬件中断的相似之处在于打断了程序执行的正常流程。
信号事件的来源分为软件信号和硬件信号:
- 硬件信号: 用户输入:比如在终端上按下组合键ctrl+C,产生SIGINT信号;硬件异常:CPU检测到内存非法访问等异常,通知内核生成相应信号,并发送给发生事件的进程;
- 软件信号: 通过系统调用: 如,发送signal信号:
kill,raise等。
发送的信号
Ctrl-C发送 INT signal (SIGINT),通常导致进程结束Ctrl-Z发送 TSTP signal (SIGTSTP); 通常导致进程挂起(suspend)Ctrl-\发送 QUIT signal (SIGQUIT); 通常导致进程结束 和 dump core.
信号的处理
内核处理进程收到的signal是在当前进程的上下文,故进程必须是Running状态。当进程唤醒或者调度后获取CPU,则会从内核态转到用户态时检测是否有signal等待处理,处理完,进程会把相应的未决信号从链表中去掉。
signal信号处理时机: 内核 ==> 信号处理 ==> 用户
- 内核态:在内核态,signal信号不起作用;
- signal信号处理: 在用户态,signal所有未被屏蔽的信号都处理完毕;当屏蔽信号,取消屏蔽时,会在下一次内核转用户态的过程中执行;
信号处理方式
进程对信号的处理方式有3种:
- 默认 接收到信号后按默认的行为处理该信号。 这种方式为多数应用采取的处理方式。
- 自定义处理 用自定义的信号处理函数来执行特定的动作
- 忽略忽略信号 接收到信号后不做任何反应。
对信号的处理动作:
- Term: 中止进程
- Ign: 忽略信号
- Core: 中止进程并保存内存信息
- Stop: 停止进程
- Cont: 继续运行进程
Linux信号命令
kill
kill命令用来终止指定的进程, 对于一个后台进程就须用kill命令来终止,我们就需要先使用ps/pidof/pstree/top等工具获取进程PID,然后使用kill命令来杀掉该进程。
命令格式
kill[参数] [进程id]
命令参数
-l信号,若果不加信号的编号参数,则使用“-l”参数会列出全部的信号名称
-a当处理当前进程时,不限制命令名和进程号的对应关系
-p指定kill 命令只打印相关进程的进程号,而不发送任何信号
-s指定发送信号
-u指定用户
killall
Linux系统中的killall用于杀死指定名字的进程(kill processes by name)。我们可以使用kill命令杀死指定进程PID的进程,如果要找到我们需要杀死的进程,我们还需要在之前使用ps等命令再配合grep来查找进程,而killall把这两个过程合二为一,是一个很好用的命令。
命令格式
killall[参数] [进程名]
命令参数
-I忽略小写
-a当处理当前进程时,不限制命令名和进程号的对应关系
-i交互模式,杀死进程前先询问用户
-s发送指定的信号
-w等待进程死亡
-e要求匹配进程名称
PKILL
pkill 与 killall 使用方法类似,用于杀死指定名称的进程
Go语言中的Signal的使用
在Go语言中,处理信号仅需要3个步骤即可完成对信号的处理
- 信号的接收:
signalChan := make(chan os.Signal,1) - 信号的监听捕获:
signal.Notify(signalChan) - 信号的触发:
signal := <-signalChan
注意事项:
SIGKILL kill -9和SIGSTOP kill -19信号可能不会被Notify方法捕获,因此无法处理这些信号。- 如果在Notify方法中没有指定信号作为参数,那么该方法将捕获所有的信号。
在Go语言中的Signal的处理
在某些场景下,如,在大量并发及,批量处理未完成时,此时需要在Go程序中处理Signal信号,比如收到SIGTERM信号后优雅的关闭程序。
实例:在一个计算场景下,有5个goroutine在处理业务,当收到
kill -15时计算完成后退出程序,kill -4不做处理。
package main
import (
"fmt"
"os"
"os/signal"
"sync"
"syscall"
"time"
)
var wg sync.WaitGroup
func exitProcess() {
fmt.Println("等待进程完成")
wg.Wait()
fmt.Println("进程退出")
}
func process(n int) {
i := n
for {
fmt.Println("process", n, ":", i)
if i > 100 {
break
}
time.Sleep(time.Second)
i++
}
fmt.Println("process", n, "finnshed")
defer wg.Done()
}
func main() {
signals := make(chan os.Signal, 1)
done := make(chan bool, 1)
signal.Notify(signals, syscall.SIGILL, syscall.SIGTERM)
go func() {
for signal := range signals {
switch signal {
case syscall.SIGTERM, syscall.SIGQUIT:
fmt.Println("kill -15 进程退出")
exitProcess()
case syscall.SIGILL:
fmt.Println("kill -4")
}
}
done <- true
}()
wg.Add(5)
for n := 0; n < 10; n++ {
go process(n)
}
fmt.Println("waiting signal...")
wg.Wait()
fmt.Println("exiting")
}
收到kill -4 信号打印kill -4

收到kill -15 信号后,带程序处理完成后退出


Go进程间通讯
Linux信号与golang中的捕获处理的更多相关文章
- Linux信号
信号本质上就是一个软件中断,它既可以作为两个进程间的通信的方式, 更重要的是, 信号可以终止一个正常程序的执行, 通常被用于处理意外情况 ,* 信号是异步的, 也就是进程并不知道信号何时会到达 $ki ...
- Linux 信号捕捉
pause函数 pause函数挂起调用它的进程,直到有任何消息到达. 调用进程必须有能力处理送达的信号,否则信号的默认部署就会发生. int pause(void); 只有进程捕获到一个信号的时候pa ...
- 僵尸进程学习 & 进程状态列表 & Linux信号学习
参考这篇文章: http://www.mike.org.cn/articles/treatment-of-zombie-processes-under-linux/ 在Linux进程的状态中,僵尸进程 ...
- Linux信号(signal) 机制分析
Linux信号(signal) 机制分析 [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核 ...
- [置顶] Linux信号相关笔记
最近又温习了一遍Linux中的信号知识,发现有很多东西以前没有注意到,就通过这篇博客记录一下,巩固一下知识点. 一,信号基础: 信号是什么?为了回答这个问题,首先要从异常说起,这里的异常不是指c++/ ...
- linux 信号列表和基本作用
我们运行如下命令,可看到Linux支持的信号列表: $ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7 ...
- Linux信号列表
我们运行如下命令,可看到Linux支持的信号列表: ~$ kill -l1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL5) SIGTRAP 6) SIGABRT 7) ...
- Golang中的信号处理
信号类型 个平台的信号定义或许有些不同.下面列出了POSIX中定义的信号. Linux 使用34-64信号用作实时系统中. 命令 man 7 signal 提供了官方的信号介绍. 在POSIX.1-1 ...
- Linux信号实践(2) --信号分类
信号分类 不可靠信号 Linux信号机制基本上是从UNIX系统中继承过来的.早期UNIX系统中的信号机制比较简单和原始,后来在实践中暴露出一些问题,它的主要问题是: 1.进程每次处理信号后,就将对信号 ...
随机推荐
- JS中EventLoop、宏任务与微任务的个人理解
为什么要EventLoop? JS 作为浏览器脚本语言,为了避免复杂的同步问题(例如用户操作事件以及操作DOM),这就决定了被设计成单线程语言,而且也将会一直保持是单线程的.而在单线程中若是遇到了耗时 ...
- 学习笔记-vue 打包去#和页面空白问题
文件资源路径是对的,但是页面空白.百度了很久找了一篇文章解决了. 1.vue项目中config文件下index.js中打包配置 build: { // Template for index.html ...
- python基础(十七):函数
在正式讲述函数之前,先给大家说明一点:编写函数就是"面向过程"的方式,编写类就是"面向对象"的方式.你如果不知道这是啥意思,至少别人提到这2个词你应该知道是在干 ...
- 【3.0 递归 Recursion 02】
[递归:阶乘] 1.寻找基本情况 对于阶乘而言,最基本的情况就是0!和1!,二者的结果都是1 我们不妨现在方法中写下这个情况,帮助我们跳出递归 if(i<=1){ return 1 ; } 接下 ...
- ASP.NET Core中使用令牌桶限流
在限流时一般会限制每秒或每分钟的请求数,简单点一般会采用计数器算法,这种算法实现相对简单,也很高效,但是无法应对瞬时的突发流量. 比如限流每秒100次请求,绝大多数的时间里都不会超过这个数,但是偶尔某 ...
- 生产环境中的redis是怎么部署的?
redis cluster,10台机器,5台机器部署了redis主实例,另外5台机器部署了redis的从实例,每个主实例挂了一个从实例,5个节点对外提供读写服务,每个节点的读写高峰qps可能可以达到每 ...
- Android Activity间跳转与传递数据
1 概述 Activity之间的跳转主要使用 startActivity(Intent intent); startActivityForResult(Intent intent,int reques ...
- jasypt-spring-boot提示Failed to bind properties
1 问题描述 在Spring Boot中使用jasypt-spring-boot进行加密,但是提示: Description: Failed to bind properties under 'spr ...
- AdaBoostClassifier参数
[AdaBoostClassifier] Adaboost-参数: base_estimator:基分类器,默认是决策树,在该分类器基础上进行boosting,理论上可以是任意一个分类器,但是如果是其 ...
- Java集合知识总结
集合概述 集合:集合是Java中提供的一种容器,可以用来存储多个数据. 集合和数组的区别: (1)数组长度的是固定的,集合的长度是可变的. (2)数组中存储的都是同一类型的元素.集合存储的都是对象,对 ...