信号是什么?
- 通知机制,用户或者OS给进程发送一定的信号,通知进程,某些事已经发生,进程可以在后续处理!
- 进程能够处理信号,则说明它具备识别信号的能力。
- 信号的产生是随机的,而进程可能在忙自己的事情,一般而言,信号的产生相对于进程而言是异步的!
一、 信号的产生
kill -l
看所有命令
1. 键盘
ctrl + c
2. 系统调用接口
- 命令行
kill -SIGNUM pid
- 函数
kill(procid, signumber); // 给进程发信号
raise(signumber); // 给自己发信号
abort(); // 给自己发6号信号退出
3. 软件条件产生信号
- 管道: 读端不光不读,而且还关闭了,写端还一直在写,则写是没有意义的!OS会自动终止写端进程,通过发信号的方式 – SIGPIPE!
- 闹钟函数(默认行为是终止进程)
alarm(1); // 设定了一个闹钟,一旦触发,就移除了
while (true)
sleep(1);
4. 硬件异常产生信号
- 例子:除0异常,nullptr写入(段错误)
- 是属于硬件上出错,除0是CPU溢出标记为为1,立即会找到谁在运行,OS完成信号发送;nullptr写入是通过虚拟地址转化为物理地址的时候,页表+MMU转化,访问越界,一定会报错。
- 如过被捕捉,这些信号会一直死循环发送,进程无法继续运行,因为硬件问题一直没有被解决!OS就会一直发信号。
结论:所有的信号,有他的来源,但最终都是被OS识别,解释,并发送的。
二、 信号的保存
PCB内部必须具有保存信号的相关数据结构(位图)内核数据结构(sigset_t)
相关概念
- 实际执行信号的处理动作称为信号递达(Delivery)
- 信号从产生到递达之间的状态,称为信号未决(Pending)。
- 进程可以选择阻塞 (Block )某个信号。
- 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
- 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作
信号集操作函数
#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
在设置信号屏蔽字的时候,要用sigprocmask函数,读取未决信号集是sigpending函数
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset)
// how SIG_BOCK/SIG_UNBLOCK/SIG_SETMASK
三、 信号的处理
man 7 signal
看信号的具体处理方式
信号捕捉
- 本质是修改对信号的处理方法
- 函数
int sigaction(int signo, const struct sigaction *act, struct sigaction *oact);
signal(2, handler);
其他
volatile 作用:保持内存的可见性,告知编译器,被该关键字修饰的变量,不允许被优化,对该变量
的任何操作,都必须在真实的内存中进行操作