该系列文章为《Linux/Unix系统编程手册》的学习笔记,由于该书太过冗长,属于工具书的类别,这里对书中的一些核心内容加以提炼和整理。 书中的编程练习这里不做展示和说明。
概念
信号:事件发生时对进程的通知机制,也称为软件中断。进程与内核都可以向某一进程发送信号,但多数信号都是源于内核。
- 标准信号:内核向进程通知事件,编号范围为1~31。
- 实时信号
信号产生后稍后将被传递给某一进程,在产生和送达期间,信号处于pending状态。信号到达后进程可根据特定信号执行以下默认操作之一:
- ignore: 忽略信号
- term: 终止(杀死)进程
- core:产生核心转储文件(虚拟内存镜像)并终止进程
- stop:停止(暂停)进程执行
- cont:恢复进程执行
进程对信号除了执行默认操作,也可设置对信号的处置:
- 采取默认行为
- 忽略信号
- 执行信号处理程序
常见标准信号
注:以下信号隐去”SIG”开头
信号名 | 编号 | 描述 | 默认行为 |
---|---|---|---|
ABRT | 6 | 系统调用abort()时终止进程 | core |
CHLD | 17 | 子进程终止时向父进程发送信号 | ignore |
CONT | 18 | 恢复已停止的进程 | cont |
HUP | 1 | 终端断开(挂机)时,内核向终端控制进程发送此信号 | term |
INT | 2 | 终端中断(如键入Ctrl+C)时,向前台进程组发送此信号 | core |
KILL | 9 | 必杀信号,信号处理程序无法将其阻塞、忽略或者捕获 | term |
PIPE | 13 | 管道断开,某一进程向管道、套接字或FIFO写入信息,若这些设备无响应阅读进程则会收到此信号 | term |
QUIT | 3 | 终端退出(如键入Ctrl+\)时向前台进程组发送此信号 | core |
SEGV | 11 | 无效的内存引用 | core |
STOP | 19 | 必停信号,信号处理程序无法将其阻塞、忽略或者捕获 | stop |
TERM | 15 | 终止进程 | term |
TSTP | 20 | 终端停止(如键入Ctrl+Z挂起字符)时向前台进程组发送此信号 | stop |
信号相关行为
改变信号处置
void ( *signal(int sig, void (*handler)(int)) )(int);
调用signal
函数可以为指定信号设置信号处置函数,handler
为指向函数的指针,也可以为下面的值来替代函数:
- SIG_DFL:将信号处置设置为默认值。
- SIG_IGN:忽略信号。
为一个信号设置了信号处置函数之后,当收到该信号时,主进程会被打断,内核将代表进程来调用处置函数,当处置函数返回时,主进程会从打断位置继续执行。
除了signal
,Linux还提供了sigaction
函数来设置信号处置,其具有更多的功能和更好的可移植性。
发送信号
int kill(pid_t pid, int sig);
调用kill(pid, sig)
函数向指定进程发送信号,pid
的值除了为进程号外还可以为下面的值:
- 0:向调用进程同组的所有进程,包括进程自身发送信号。
- -1:向调用进程有权发送的所有进程,除去init进程和进程自身外发送进程,也叫广播信号。
- -pid:向组id等于pid的进程组内所有下属进程发送信号,常用于作业控制。
小tip:如果sig
指定为0,则无信号发送,但可以用来判断指定进程号的进程是否存在,不存在会调用失败。
int raise(int sig);
调用raise(sig)
可向自身发送信号,等同于kill(getpid(), sig)
。向自身发送的信号没有pending状态,将立即传递。
int killpg(pid_t pgrp, int sig);
调用killpg(pgrp, sig)
向某个进程组内所有成员发送信号,等同于killpg(-pgrp, sig)
。
阻塞信号传递(信号掩码)
信号集是用来表示多个信号的数据结构。
int sigemptyset(sigset_t *set);
int sigaddset(sigset_t *set, int sig);
int sigdelset(sigset_t *set, int sig);
int sigismember(sigset_t *set, int sig);
上述四个系统调用分别用于创建信号集、增加/删除信号集里的信号、检测信号是否位于信号集中。
内核会为每个进程维护一个信号掩码,即一组信号,阻塞这组信号对该进程的传递。某一信号如果遭到阻塞,将延后对该进程的传递,信号处于pending状态,直到信号从进程的信号掩码中移除,从而解除阻塞。
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
调用sigprocmask(how, set)
修改当前进程的进程掩码,修改方式由how参数决定。oldset
如果不为空,将用于返回之前的信号掩码。
- SIG_BLOCK:将
set
指向的信号集内的信号增加到信号掩码中。 - SIG_UNBLOCK:将
set
指向的信号集内的信号从信号掩码中移除。 - SIG_SETMASK:将
set
指向的信号集内的信号设置为信号掩码,即覆盖。
处于等待状态下的信号不会排队,即如果同一个信号被阻塞多次,在解除阻塞后也只会向进程传递一次。
信号的产生方式
- 同步产生:由进程自身执行代码产生的信号,如向自身发送信号或执行进程引发了硬件异常,这类信号将立即传递。
- 异步产生:由内核或另一进程向进程发送信号,这类信号的传递时机为进程正在执行且发生由内核态到用户态的下一次切换时。
(End)