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.进程每次处理信号后,就将对信号 ...
随机推荐
- python mac地址计算
思路是10/16进制的转换和字符串的处理 开始造轮子 1.判断是否是mac地址 正则匹配是否符合条件 1 import re 2 3 def isMac(string): 4 preg = re.co ...
- [^ ] 跟 [! ] 差在哪?-- Shell十三问<第十四问>
[^ ] 跟 [! ] 差在哪?-- Shell十三问<第十四问> 这道题目说穿了, 就是要探讨 Wildcard(通配符)与 Regular Expression(正则表达式)的差别的. ...
- 201871030108-冯永萍 实验二 个人项目— D{0-1}背包问题项目报告
项目 内容 课程班级博客链接 https://edu.cnblogs.com/campus/xbsf/2018CST 这个作业要求链接 https://www.cnblogs.com/nwnu-dai ...
- UnitFourSummary
目录 第四单元架构设计 taskOne taskTwo taskThree 四个单元架构设计与OO方法理解的演进 四个单元架构设计 UnitOne UnitTwo UnitThree UnitFour ...
- PAT B1033 旧键盘上的几个键又毁坏了,于是在输入一段文字时,对应得的字符就不会出现。
题中可用的字母包括大小写(给出的坏键的字母,全为大写),数字,"_" "," "." "-" "+" ...
- BBR拥塞算法的简单解释
TCP BBR的ACM论文中,开篇就引入了图1,以此来说明BBR算法的切入点: 为何当前基于丢包探测的TCP拥塞控制算法还有优化空间? BBR算法的优化极限在哪儿? 图1 为了理解这张图花了我整整一个 ...
- 「最强」Lettuce 已支持 Redis6 客户端缓存
Redis 客户端缓存 缓存的解决方案一般有两种: [L1] 内存缓存(如 Caffeine.Ehcache) -- 速度快,进程内可用,但重启缓存丢失,出现缓存雪崩的问题. [L2]集中式缓存(如 ...
- Linux保护机制和绕过方式
Linux保护机制和绕过方式 CANNARY(栈保护) 栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让shellcode能够得到执行.用C ...
- 关gzip压缩,我有新发现
1 gzip的压缩效果是立竿见影的: 2 网站是否开启gzip的查看方式 2.1 打开Chrome浏览器,按 F12打开调试面板 2.2 切换到network页签,在网络请求列表的表头,鼠标右键==& ...
- 手把手教你搭建自己的Angular组件库 - DevUI
摘要:DevUI 是一款面向企业中后台产品的开源前端解决方案,它倡导沉浸.灵活.至简的设计价值观,提倡设计者为真实的需求服务,为多数人的设计,拒绝哗众取宠.取悦眼球的设计.如果你正在开发 ToB 的工 ...