关于青春
是前进 奔驰 追寻 飞跃三万英尺后
抽离腾空的寄托~
目录:
1.信号概念
2.信号产生
- 通过终端按键
- 调用系统函数向进程发信号
- 软件条件产生信号
- 硬件异常产生
3.信号在进程中注册/注销
4.信号的处理
5.信号的阻塞
6.信号捕捉
- 可重入信号
>>信号概念
信号:是一个软件中断;通知进程发生了某件事情,中断进程当前操作;让进程去处理这事件信号用于通知一个事件的发生,会打断当前操作,去处理这个事件;【当然有一个前提:必须识别这个信号】
信号种类有很多,每个代表不同事件
信号是有生命周期的:信号产生—>在进程中注册->在进程中注销->信号的处理 阻塞
>>信号的产生
操作系统中定义信号种类:
通过查看信号种类:kill -| kill杀死进程的原理:向进程发送信号-通知事件
信号共有62种,两大类型:
非可靠信号:1~31号信号
可靠信号:34~64号信号
信号的产生:
硬件产生:ctrl+c ctrl+| ctrl+z
软件产生:kill -signum pid 向进程发送一个signum信号
kill(int pid,int signum) raise(int signum)
>>信号在进程中的注册
在进程pcb中做标记,标记进程收到了哪些信号
未决:是一种状态,信号产生到处理之前所处的状态;非可靠信号注册:判断pcb中的pending位图中相应信号是否已经注册(位图是否置1);若未注册,则位图修改为1,向sigqueue链表中添加一个信号节点;若已经注册,则不作任何操作(事件丢失);
可靠信号注册:不管信号是否已经注册,都会向链表中添加一个新的信号节点(事件不会丢失);
>>信号在进程中的注销
非可靠信号:节点只有一个,注销就是删除节点,位图置0;
可靠信号:节点有可能有多个,注销就是删除一个节点,判断链表中是否还有相同信号的节点;若没有则位图置0;否则位图不变依然需要标记有这个信号待处理。
>>信号的处理
信号的处理并不是立即被处理;而是选择一个合适的实际去处理信号;进程的运行从内核返回用户态的时候;
进程如何从用户态切换到内核态:发起系统调用:程序异常;中断
进程运行的代码若是库函数或者用户自己写的函数,就是说进程当前运行在用户态;
信号处理有多种方式:
默认方式--既定义好的处理方式
忽略处理方式--处理动作中什么都没做
自定义处理方式--用户自己确定信号如何处理--自定义信号的处理函数替代原有的处理函数
如何修改信号处理的方式
sighandler_t signal(int signum, sighandler_t handler);
signum: 信号编号--替换signum这个信号的处理函数
handler:函数指针,用户传入的处理函数
SIG_DFL:信号的默认处理动作
SIG_IGN:信号的忽略处理动作
typedef void(*sighandler_t)(int);
如果在休眠状态下有信号,休眠状态会被打断
>>信号的阻塞
阻止信号被递达--信号依旧可以注册,知识暂时不处理;
递达:一个信号处理的动作;
在pcb中还有一个集合-阻塞信号集合--标记哪些信号暂时不被处理
接口:
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
SIG_BLOCK 向阻塞集合中加入set集合中的信号 block = mask | set
SIG_UNBLOCK 从阻塞集合中移除set集合的信号 block = mask & (~set)
SIG_SETMASK 将set集合的信号设置为阻塞集合 block = set
oldset 用于保存修改前,阻塞集合的信号
在所有的信号中,9号信号SIGKILL和19号信号SIGSTOP,无法被阻塞,无法被自定义,无法被忽略
在所有的信号中,9号信号SIGKILL和19号信号SIGSTOP,无法被阻塞,无法被自定义,无法被忽略。
>>信号捕捉
捕捉流程:
发起系统调用--->切换到内核态(调用功能完成)--->do signal()处理未处理的信号--->sigcb()
信号自定义回调完毕--->sigreturn返回内核态--->(如果有信号还没处理继续调用do signal();
没有信号待处理就sys_sigreturn返回用户态
>>可重入信号和不可重入信号
函数的重入:多个执行流程同时进入相同的函数
函数的可重入与不可重入:
可重入:多个执行流程同时执行进入相同的函数,不会造成数据二义性以及代码逻辑混乱;
不可重入:多个执行流程同时进入相同的函数,有可能造成数据二义性以及代码逻辑混乱。
关键:考虑这个函数是否对临界资源(全局数据)进行了非原子操作