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.进程每次处理信号后,就将对信号 ...
随机推荐
- 【LiteOS】LiteOS任务篇-源码分析-创建任务函数
目录 前言 链接 参考 笔录草稿 部分源码分析 源码分析 LOS_TaskCreate函数 LOS_TaskCreateOnly函数 宏 OS_TCB_FROM_PENDLIST 和 宏 LOS_DL ...
- Android Studio 有关 RecycleView 的使用
•导入相关包 右击File->Project Structure: 搜索 com.android.support: 找到 recyclerview: 导入好后 Sync Now 同步一下,到这 ...
- 期末考试复习c#时总结的抽象类与接口的一些区别
抽象类: (1)抽象类中可以定义抽象方法,属性,变量 (2)抽象类的派生类必须实现所有的抽象方法.要求所有的派生非抽象类都要用override重写实现抽象方法. (3)抽象类可以存放抽象方法,属性,也 ...
- (十四)docker exec 详解
1. 作用 在运行的容器中执行命令 2. 语法 docker exec [OPTIONS] CONTAINER COMMAND [ARG...] OPTIONS说明: -d :分离模式: 在后台运行 ...
- 通过Python实现对SQL Server 数据文件大小的监控告警
1.需求背景 系统程序突然报错,报错信息如下: The transaction log for database '@dbname' is full. To find out why space in ...
- P2P技术(一):NAT
1.NAT由来 NAT是一项神奇的技术,说它神奇在于它的出现几乎使IPv4起死回生.在IPv4已经被认为行将结束历史使命之后近20年时间里,人们几乎忘了IPv4的地址空间即将耗尽这样一个事实--在新技 ...
- Java文档注释全攻略
注释:注释起到对代码标注和解释的作用,如果你去看看JDK源码,会发现他们有许多的注释,而且注释是比代码还要多的,可见为代码添加注释是非常重要的,写好注释能让别人更加容易看懂你的代码,注释可以分为以下三 ...
- 033- while循环语句
语法 初始化语句; while(boolean表达式) { 循环体语句; 控制条件语句; } 执行过程: 只要boolean表达式里面的结果是true,就会执行while大括号里面的语句,直到bool ...
- Vue2.0组件之间通信
Vue中组件这个特性让不少前端er非常喜欢,我自己也是其中之一,它让前端的组件式开发更加合理和简单.笔者之前有写过一篇Vue2.0子父组件通信,这次我们就来聊一聊平级组件之间的通信. 首先我们先搭好开 ...
- 关于Eclipse Debug断点调试出现 Search not found 页面的解决办法
1. 在代码中鼠标右键 Debug AS ---> Debug Configurations... ----> 找到Source选项 ---> 点击add ---> 选择 j ...