1.信号简介
1.用户输入命令,在shell下启动一个进程。
2.按下ctrl+c,这是键盘输入产生一个硬件中断,被os获取,解释成信号,发送给前台进程
3.前台进程收到信号,进而引起进程退出。
ps:信号是进程之间事件异步通知的一种方式,属于软中断
使用命令 kill -l
可以查看信号
2.产生信号的四种方式
- 键盘按键产生(ctrl + c…)
- 硬件条件产生
硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。- 软件条件产生
- 系统调用产生(kill)
3.信号的处理方式
- 忽略
- 默认处理行为,终止进程
- 捕捉信号,
4.信号的捕捉
捕捉一下2号信号使ctrl+c失效,但是可以使用其他信号中断(ctrl+\)
#include<signal.h>
#include<stdio.h>
void hehe(int sig){
printf("signal=%d\n",sig);
}
int main()
{
signal(2,hehe);
while(1);
return 0;
}
ps:我们在C/C++当中除零,内存越界等异常,在系统层面上,是被当成信号处理的
5.信号捕捉流程
信号处理函数是一个独立的执行流,和原来的用户代码之间没有相互调用的关系 信号处理函数执行过程中,原有的执行流就在等待
6.阻塞信号
有时候信号不会立刻被处理,而是等到何时的时机在进行处理
- 实际执行信号的处理动作称为信号递达(Delivery);
- 信号从产生到递达之间的状态,称为信号未决(Pending);
- 进程可以选择阻塞 (Block )某个信号;
- 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作;
- 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作;
7.可重入函数
一个函数如果在多个执行流中被调用,就可能出现问题‘
一个函数如果可重入,就意味着在多个执行流中调用是没问题的。
一个函数如果不可重入,在多个执行流中调用就是有问题的。
如果一个函数使用了全局变量/静态变量,不可重入
如果一个函数调用了不可重入函数,也是不可重入的,
如下创建20次子进程应该结束20次子进程打印20次child exit,可实际没有退出完,出现了僵尸进程,是因为在同时收到多个信号,只执行了一次。
#include<signal.h>
#include<stdio.h>
#include<sys/wait.h>
#include<unistd.h>
#include<stdlib.h>
void hehe(int sig)
{
printf("child exit\n");
wait(NULL);
}
int main()
{
//signal(SIGCHLD, SIG_IGN);
signal(17,hehe);
int i = 0;
for(;i<20; ++i){
pid_t ret = fork();
if(ret == 0){
printf("hehehepid %d\n",getpid());
exit(0);
}
}
while(1)
{
sleep(1);
}
return 0;
}
//signal(SIGCHLD, SIG_IGN); 捕获信号之后