信号简介
信号是信息的载体,Linux/UNIX 环境下,古老、经典的通信方式, 现下依然是主要的通信手段。 信号在我们的生活中随处可见,例如:
- 古代战争中摔杯为号;
- 现代战争中的信号弹;
- 体育比赛中使用的信号枪......
信号的特点
- 简单
- 不能携带大量信息
- 满足某个特点条件才会产生
信号的机制
进程A给进程B发送信号,进程B收到信号之前执行自己的代码,收到信号后,不管执行到程序的什么位置,都要暂停运行,去处理信号,处理完毕后再继续执行。与硬件中断类似——异步模式。但信号是软件层面上实现的中断,早期常被称为“软中断”。
每个进程收到的所有信号,都是由内核负责发送的。 进程A给进程B发送信号示意图:
信号的状态
信号有三种状态:产生、未决和递达。
信号的产生
- 按键产生,如:Ctrl+c、Ctrl+z、Ctrl+\
- 系统调用产生,如:kill、raise、abort
- 软件条件产生,如:定时器alarm
- 硬件异常产生,如:非法访问内存(段错误)、除0(浮点数例外)、内存对齐出错(总线错误)
- 命令产生,如:kill命令
未决
- 产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态。
递达
- 递送并且到达进程。
信号的处理方式
- 执行默认动作
- 忽略信号(丢弃不处理)
- 捕捉信号(调用用户的自定义的处理函数)
信号的特质
信号的实现手段导致信号有很强的延时性,但对于用户来说,时间非常短,不易察觉。
Linux内核的进程控制块PCB是一个结构体,task_struct,除了包含进程id,状态,工作目录,用户id,组id,文件描述符表,还包含了信号相关的信息,主要指阻塞信号集和未决信号集。
注:表示PCB的task_struct结构体定义在:/usr/src/linux-headers-4.4.0-97/include/linux/sched.h:1390
信号的四要素
通过 man 7 signal
可以查看信号相关信息
信号的编号
- 使用
kill -l
命令可以查看当前系统有哪些信号,不存在编号为0的信号。其中1-31号信号称之为常规信号(也叫普通信号或标准信号),34-64称之为实时信号,驱动编程与硬件相关。
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
信号的默认处理动作
- Term:默认作是终止进程。
- Ign:默认作是忽略该信号。
- Core:默认作是终止进程并转储核心,生成Core文件。(查验死亡原因,用于gdb调试)
- Stop:默认作是停止进程。
- Cont:默认作是继续该过程(如果当前已停止)。
特别需要注意的是:The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
信号的名称
signal | standard | action | comment |
---|---|---|---|
SIGHUP | 1 | Term | 终端挂起或者控制进程终止 |
SIGINT | 2 | Term | 键盘中断(Ctrl+c) |
SIGQUIT | 3 | Core | 键盘退出(Ctrl+) |
SIGILL | 4 | Core | 非法指令 |
SIGTRAP | 5 | Core | 断点异常 |
SIGABRT | 6 | Core | 由abort(3)发出的退出指令 |
SIGBUS | 7 | Core | 总线错误 |
SIGFPE | 8 | Core | 浮点运算错误 |
SIGKILL | 9 | Term | 无条件终止进程 |
SIGUSR1 | 10 | Term | 用户保留 |
SIGSEGV | 11 | Core | 无效内存引用 |
SIGUSR2 | 12 | Term | 用户保留 |
SIGPIPE | 13 | Term | 消息管道损坏 |
SIGALRM | 14 | Term | 定时器超时 |
SIGTERM | 15 | Term | 软件终止 |
SIGSTKFLT | 16 | Core | 协处理器堆栈错误 |
SIGCHLD | 17 | Ign | 子进程状态改变 |
SIGCONT | 18 | Cont | 如果进程已停止,则使其继续 |
SIGSTOP | 19 | Stop | 非终端停止进程 |
SIGTSTP | 20 | Stop | 终端停止进程 |
SIGTTIN | 21 | Stop | 后台进程读终端 |
SIGTTOU | 22 | Stop | 后台进程写终端 |
SIGURG | 23 | Ign | 套接字上有紧急数据 |
SIGXCPU | 24 | Core | 进程执行时间超过限制 |
SIGXFSZ | 25 | Core | 文件长度超过限制 |
SIGVTALRM | 26 | Term | 虚拟定时器超时 |
SIGPROF | 27 | Term | 组定时器超时 |
SIGWINCH | 28 | Ign | 窗口大小改变 |
SIGIO | 29 | Term | 描述符上可以进行I/O操作 |
SIGPWR | 30 | Term | 电源故障 |
SIGSYS | 31 | Core | 非法系统调用 |
产生信号的事件
几个常用到的信号
SIGINT、SIGQUIT、SIGKILL、SIGSEGV、SIGUSR1、SIGUSR2、SIGPIPE、SIGALRM、SIGTERM、SIGCHLD、SIGSTOP、SIGCONT
信号集
Linux内核的进程控制块PCB是一个结构体,这个结构体里面包含了信号相关的信息,主要有阻塞信号集和未决信号集。
阻塞信号集和未决信号集
- 阻塞信号集中保存的都是被当前进程阻塞的信号。若当前进程收到的是阻塞信号集中的某些信号,这些信号需要暂时被阻塞,不予处理。
- 信号产生后由于某些原因(主要是阻塞)不能抵达,这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态;若是信号从阻塞信号集中解除阻塞,则该信号会被处理,并从未决信号集中去除。
阻塞信号集是当前进程要阻塞的信号的集合,未决信号集是当前进程中还处于未决状态的信号的集合,这两个集合存储在内核的PCB中。
下面以SIGINT为例说明信号未决信号集和阻塞信号集的关系:
- 当进程收到一个SIGINT信号(信号编号为2),首先这个信号会保存在未决信号集合中,此时对应的2号编号的这个位置上置为1,表示处于未决状态;在这个信号需要被处理之前首先要在阻塞信号集中的编号为2的位置上去检查该值是否为1:
- 如果为1,表示SIGNIT信号被当前进程阻塞了,这个信号暂时不被处理,所以未决信号集上该位置上的值保持为1,表示该信号处于未决状态;
- 如果为0,表示SIGINT信号没有被当前进程阻塞,这个信号需要被处理,内核会对SIGINT信号进行处理(执行默认动作,忽略或者执行用户自定义的信号处理函数),并将未决信号集中编号为2的位置上将1变为0,表示该信号已经处理了,这个时间非常短暂,用户感知不到。
- 当SIGINT信号从阻塞信号集中解除阻塞之后,该信号就会被处理。