信号是进程之间的事件异步通知的一种方式,属于软中断.–通知时间的发生(选择合适时机去打断当前操作,去处理信号)打断进程当前的阻塞操作.
功能 : 通知事件的发生
使用kill - l 命令可以察看系统定义的信号列表
- 每一个信号都有一个编号和一个宏定义名称.
- 62种信号. 1 - 31:非可靠信号(信号可能会丢失 --丢事件)非实时信号 ; 编号34以上的都是实时信号,可靠信号(信号不会丢失)
信号的生命周期
产生 -> 注册 -> 注销 -> 处理
信号的产生
硬件: Ctrl + c ; Ctrl l + l ; Ctrl + z ; “l”
软件: kill命令 kill(), raise( ), abort( ), alarm();
raise()函数相当于给当前进程发送指定的信号(自己给自己发送信号)
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
这两个函数都是成功返回0,错误返回-1。
abort()函数使当前进程接收到信号而异常终止
#include <stdlib.h>
void abort(void);
像exit函数一样,abort函数总是会成功的,所以没有返回值。
alarm()函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。
core dumped – 核心转储 – 程序异常退出时保存程序运行信息 – 事后调试
core dumped – 默认关闭 – 占磁盘资源,安全性考虑
ulimit -a 查看core dump 是否开启
ulimit -c 1024 设置转储文件大小,开启核心转储
gbd ./loop --> core-file core.pid --> bt
信号在进程中注册:
修改未决信号对应位图,添加 sigqueue 节点
未决信号 : 注册了但是没有被处理完的信号
未决: 是一个信号从产生到信号处理之间所处的状态
sigset_t : 信号集合(位图)
pcb -> struct sigpeding -> sigset_t signai
sigset_t 结构体的认识:
unsigned long int_val[_SIGSET_NWORDS]是一个128位的位图,用于对信号是够到来做标记
sifqueue是一个链表 – 信号到来之后会阻止一个对应信号的节点,添加到链表中
非可靠信号 : 如果位图已经标记,将什么都不做(每个信号只添加一个节点,信号到来的同时,有可能会丢失时间) ,否则添加节点, 标记位图.
即判断pending 信号集合位图相应位是否为0, 为0则为信号阻止sigqueue节点添加到链表中,并且pending位图置1; 若为1(信号已经被注册过还没有被处理), 则什么都不用做(等于丢掉)
可靠信号 : 不管位图是否标记,都会为每个到来的信号组织一个节点添加到链表中; 标记位图
即 不管位图是否为1 ,阻止节点,添加到链表中,并且位图置 1 .
在进程中注销
删除信号的sigqueue节点 ,修改位图
可靠节点注销: 删除节点,判断链表中是否具有相同节点,如果没有,位图置0 , 否则位图置1
非可靠信号注销 : 因为非可靠信号的信号节点只有一个,因此删除节点,修改位图为 0
信号的处理
默认 SIG_DFL signal(int signum, sighanler_t hanle)
忽略 SIG_IGN 修改信号的处理方式
自定义
进程如何从用户态切换到内核态运行?:系统调用,中断,异常
main()系统系统调用\中断\异常->完成内核功能从内核态返回用户态的时候判断是否有信号待处理->有,且处理方式是忽略/默认将直接在内核态完成,否则,如果是自定义方式,则返回用户态执行用户自定义信号回调函数,执行完毕后返回内核态,如果没有信号处理了,则返回主控流程
信号的阻塞
在进程pcb中的block信号集合中,在程序初始化的时候标记要阻塞的信号,当这些被标记的信号注册之后暂时不做处理,直到解除阻塞( 从block集合中移除)
信号集合—sigset_t long int_val[16],使用数组作为位图进行标记 task_struct->sigset_t block
暂时阻止信号被递达—信号依然可以注册,但是暂时不处理, 解除阻塞后才会处理
信号的递达: 动作 - 信号的处理
信号的未决 : 状态 - 信号从产生到处理之前所处的状态
在所有SIGKILL SIGSTOP信号无法被阻塞.无法被自定义处理,无法被忽略. 可靠信号不会丢失,非可靠信号可能会丢失
有两个信号无法被自定义处理,无法阻塞,无法忽略:SIGHILL-9 SIGSTOP-19
接口:
sigprocmask 对阻塞信号集合操作
sigemptyset 清空信号集合
sigaddset 向集合中添加指定信号
sigfillset 向集合中添加所有信号
sigdelset 向集合中删除执行信号
sigpending 获取当前进程未决信号
sigismember 判断信号时候再集合中
竞态条件: 因为运行时序而造成数据竞争 – 导致数据二义性
函数的可重入与不可重入
一个函数在不同的执行流下是否可以被重复利用,并且不会出现问题; 有可能出问题,这个函数就是不可重入函数,否则就是可重入函数
函数中所完成的操作非原子性操作 – 并且操作的数据是一个全局数据
如果一个数据中操作了全局性数据, 并且这个操作不是原子性操作, 并且这个操作不受保护; 则这个函数就是一个不可重入函数
不可重入函数 : 不能再多个时序的运行中重复调用(重复调用有可能会造成数据二义性)
可重入函数 : 在多个时序的运行中重复调用,不会造成异常影响(数据二义性问题)
不可重入函数 : malloc / free
关键字:volatile—修饰变量—保存变量内存可见性—防止编译器过度优化代码—每次都从内存中重新获取变量数据
SIGCHID信号:
1. 死等—wait waitpid
因为子进程退出后,操作系统通过SIGCHLD信号通知父进程,由于SIFCHLD信号处理方式是忽略, 因为以前父进程不知道子进程何时退出,因此调用wait死等,浪费父进程.
2.修改信号处理方式 在信号的回调函数中调用wait/waitpid处理子进程退出事件,同时在信号中可以自定义SIGCHLD信号的处理方式,在信号回调函数中调用wait子进程退出,向父进程发送SIGCHLD信号,触发信号回调函数,在函数中执行wait因为SIGCHLD是非可靠信号,如果同时偶遇多个进程退出,有可能信号会丢失,导致sigcb只会被回调一次,只处理了一个子进程退出,其他退出的子进程成为僵尸进程;因此再一次回调中要将所有的僵尸进城全部处理.
Sigcb(){While(waitpid(-1,NULL,WNOHANG) > 0)}(“>0”表示有子进程退出)
处理退出的子进程直到不能处理