Linux Signal 信号机制
基本概述
基础概念: Unix信号利用进程间通信向进程发送信号,可使接收进程受信号影响(终止 挂机 继续)
信号发生方式
- 命令发出信号:
kill - 信号编号 进程id kill -9 1234
kill -l 查看系统支持的信号
其中 1 - 31 Unix经典信号 软件开发工程师
34 - 64 实时信号 驱动开发
32, 33 隐藏保留 给NTPL线程库使用
- 函数产生信号
kill(pid_t pid, int signo) //向任意进程发送信号
raise(int signo) //向调回函数发送信号
abort(void) //向调用进程发送特定的SIGABRT信号
- 终端组合按键产生信号 : 操作系统中前台进程永远只有一个(可交互程序)
ctrl + c 内核向前台进程发送 SIGINT(2)
ctrl + \ 终止退出唯一前台进程 SIGQUIT(3)
ctrl + z 挂起进程 SIGTSTP(20)
jobs 查看作业
fg (frontground) + 作业id 将作业唤醒至前台继续执行
bg (background) + 作业id 将作业唤醒至后台继续执行
- 硬件异常产生信号 :
段错误(segmentation fault) : 非法操作内存,内核向该进程发送SIGEGV(11),将其杀死
浮点数例外(floating point exception) : CPU违规运算(cpu / 0),内核向该进程发送SIGPE(8),将其杀死
总线错误 : 调度异常,控制异常,并发异常,内核向该进程发送SIGBUS(7),将其杀死
- 软条件产生信号
定时器 : 使用alarm定时,定时到时后,内核向定时进程发送SIGALRM(14),通知其定时到时(杀死进程)
管道读端关闭,写端向管道写数据,内核向写端发送SIGPIPE(13),杀死写进程
信号的三种行为和五种默认处理动作
SIG_DFL默认行为
SIG_IGN忽略行为 : SIGCHLD 该信号默认的处理动作IGN忽略(父子进程使用,子进程结束变为僵尸进程,内核向父进程发送SIGCHLD通知和父进程回收)
SIG_ACTION捕捉行为
使信号失效的三种方式
1.改变信号行为 将默认行为改为忽略行为,可以使信号失去原本的作用
2.改变信号行为 将默认行为改为捕捉行为,可以使信号失效
3.屏蔽/阻塞信号 使信号无法递达,不会对进程造成影响(被屏蔽信号没有丢失,某一刻接触屏蔽,依旧可以送达,影响进程)
信号失效方式的区别
阻塞信号: 信号未递达
忽略信号: 信号已递达,无处理动作
捕捉信号: 信号已递达,有处理动作
高权信号(直接为内核服务,只要发出必然递达)
SIGLKILL(9) 只要发出必然杀死进程,无法屏蔽,捕捉和忽略
SIGSTOP(19) 只要发出必然挂起,无法屏蔽,捕捉和忽略
信号捕捉实际操作流程
1.定义初始化信号行为结构体
struct sigaction act
act.sa_handler = 选择信号对应行为 SIG_DFL SIGIGN ,捕捉函数地址
void (*sa_handler)(int) 根据要求定义捕捉函数
act.sa_flags = 0 根据捕捉函数接口,调整flags选项,handler flags = 0
act.sa_mask 临时屏蔽字,多个信号绑定一个捕捉函数,为了避免这个信号同时触发,捕捉函数调用产生功能异常,可以设置在处理当前信号时,临时屏蔽其他信号
2.定义实现捕捉函数
3.将自定义信号行为替换默认行为
sigaction(int signo, struct sigaction * new_act, struct sigaction * old_act);
无论是传出屏蔽字或者信号行为,都是为了某一刻还原进程状态,如果不需要还原进程状态则不需要传出
捕捉函数的执行过程
信号捕捉函数的执行过程:进程通常工作与用户层,当信号到达进程,抵达内核层,无法实时处理,只有进程切换到内核空间,才可以检测到待处理信号并进行处理
可以通过三种方式进行进程状态切换:系统调用,中断,异常 (cpu权级切换,用户空间cpu权限低限制大,内核空间权限高可以访问系统所有资源)
(内核执行捕捉函数 消耗进程资源)
信号捕捉,可重入与不可重入:某个全局任务,main先执行,产生信号中断处理信号,捕捉函数也执行该任务,而且可以执行完,执行后回到main函数上次被中断的位置继续执行 (全局任务 main先做 捕捉函数先做完)
不可重入函数: 函数内部使用了全剧资源(全局或静态),如果信号捕捉函数与main函数同时调用不可重入函数,会导致冲突或异常
可重入函数: 函数内部没有全局资源,信号捕捉技术可以放心使用