信号
一、信号概念
是一种事件通知机制,通知进程发生了某个事件,打断进程当前的操作然后去处理这个事件。
种类: kill -l 查看linux中的信号种类—62种
非可靠/非实时信号:1-31
可靠/实时信号:34-64
二、生命周期
1、信号的产生
kill -signum pid 命令
kill杀死一个进程原理:给进程发送一个能让进程退出的信号。
int kill(pid_t pid, int sig); 给指定进程发送指定信号;
int raise(int sig); 给自己发送指定的信号;
void abort(void); 给自己发送sigabrt信号;
unsigned int alarm(unsigned int s); s秒后发送sigalrm信号给自己。
硬件: ctrl+c - 2; ctrl+\ - 3; ctrl+z – 20
2、信号的注册
让进程知道自己收到了某个信号/事件需要处理,在进程中做标记。
在pcb中有个未决信号集合(位图),用于标记进程信号。
(1)非可靠信号的注册
1-31号信号若没有注册过则注册,
已注册则什么都不做(信号丢弃)。
(不会同时注册第二次).
(2)可靠信号的注册
34-64号信号不管是否注册,位图置1,
向sigqueue链表加一个节点。
3、信号的注销
删除待处理的未决信号(pending位图中)
一个是位图,另一个是链表:
- 位图:主要是为了标记是否收到了某个信号
- 链表:统计收到多少个相同信号
(1)非可靠信号注销
因为非可靠信号不会重复注册,链表中最多只有一个节点,
因此删除一个节点,并且对应位图置0。
(2)可靠信号的注销
因为可靠信号有可能存在多个相同信号节点,
因此删除一个节点后,若还有相同节点则位图不变,否则位图置0。
4、信号的处理
运行信号的处理函数(每个信号的处理函数不同)
信号的处理函数(处理方式)是可以修改的
处理方式种类:
(1)默认处理
系统中针对每个信号定义好的处理方式.
(2)忽略处理
收到了信号处理方式就是忽略.
(3)自定义处理
用户自定义信号处理函数然后进行替换.
sighandler_t signal(int signum, sighandler_t handler);
signum:要修改处理方式的信号的值
handler:函数指针 SIG_DFL-默认/SIG_IGN-忽略
typedef void(*sighandler_t)(int);
自定义处理方式的信号捕捉流程
- 一个用户进程无法直接访问内核空间,只能通过系统调用进行访问,当程序通过系统调用访问内核空间的过程称之为运行在内核态。
(1) 程序运行因为中断\异常\系统调用从用户态切换到内核态.
(2) 完成内核处理功能后,在返回用户态主控流程之前检测是否有信号待处理,有则处理信号.
(3) 默认和忽略在内核中完成,自定义需要到用户态运行.
(4) 信号的自定义处理函数运行完毕,返回内核态.
5、信号的阻塞
阻止当前未决信号的处理
(有信号注册了但当前不处理,等到解除阻塞之后再处理)在pcb中,标记哪些信号来了但是暂不处理
block阻塞信号集合:
- 添加了哪个信号,则表示该信号来了暂不处理.
接口:
int sigprocmask(int how,sigset_t *new,sigset_t *old);
功能:对调用进程的阻塞信号集合进行操作。
how:
SIG_BLOCK block |=new; 向block集合添加new信号
SIG_UNBLOCK block &= ∽new;从block中移除new信号
SIG_SETMASK block = new; 将new设置为block
new:要添加/移除阻塞的信号集合;
old:用于接收修改前block中的信号(多数用于还原)
示例:
1.先修改指定信号的处理方式 signal(sig,handler)
2.阻塞指定的/所有信号 sigprocmask(SIG_BLOCK)
3.等待用户按下回车 getchar()
4.解除阻塞 sigprocmask(SIG_UNBLOCK)
sigset_t new;
sigemptyset(sigset_t *set);清空
sigaddset(sigset_t *set, int sig);添加指定信号到集合
sigfillset(sigset_t *set);添加所有信号到集合中
sigdelset(sigset_t *set, int sig);从集合中移除指定信号
sigismember(sigset_t *set, int sig);判断信号是否在集合中
在进程中,有两个信号是不可被阻塞,不可被修改处理方式的:
SIGKILL 、 SIGSTOP
(1)一个进程kill杀不死原因是什么?
- 僵尸进程
- 信号被修改处理方式
- 信号被阻塞
(2)应用
- 僵尸进程的处理- -进程等待
子进程退出后,父进程为什么不关心子进程退出状态?
本质:子进程退出后给父进程发送了一个信号-SIGCHLD,
然而SIGCHLD信号的默认方式为忽略,
相当于父进程实际收到了通知但是什么都没做。
sigcb(){while(waitpid(-1,NULL,WNOHANG)>0);}//默认忽略
signal(SIGCHLD,SIG_IGN);//用户的显示忽略
- 所有管道读端被关闭则write触发异常- -SIGIPE
三、关键字volatile
作用
用于修饰变量,保持变量内存可见性
防止编译器过度优化;
cpu处理数据时,总是保证重新从内存中加载数据进行处理
四、函数的可重入与不可重入
1. 函数的重入
一个函数在不同执行流程中同时进入执行
- 可重入函数:函数重入后不会造成逾期之外的结果。
- 不可重入函数:函数重入后可能出现逾期之外的结果。
2.函数是否可重入的判断基准点
一个函数内部如果对全局数据进行了不受保护的非原子操作,
则该函数为不可重入函数。