九. 信号
信号通知进程产生了某个事件。信号已经被定义过,如下面所示:
2号信号SIGINT默认为ctrl+c
9号信号强制退出,不允许修改响应方式。
17号信号为子进程结束后,默认给父进程发送该信号。
信号的本质
对于每个进程有一个进程控制块PCB。
PCB中有个属性为long signal。
00000000 00000000 00000000 00000000
当接收到1号信号时,将signal的第二位改为1,即
01000000 00000000 00000000 00000000
进程有一个表叫做信号响应表,即对响应的信号进行响应,当收到信号后,按照信号响应表进行处理,之后将signal被修改为1的那一位重新修改为0。
0 SIG_DFL
1 SIG_DFL->1 fun//signal(1,fun)将SIG_DFL改为fun
2 SIG_DFL
3 SIG_DFL
4 SIG_DFL
.......
调用fun后,将long signal重新置为0.
00000000 00000000 00000000 00000000
发送信号
向pid进程发送sig信号。
系统的kill默认发送15号信号。
kill 6925//给6925进程发送15号信号
kill -9 4925//给4925进程发送9号信号
ps -ef|grep sleep//将所有进程信息放入管道,之后过滤其他,剩余拥有sleep的进程信息
//kill()函数,给pid发送sig信号,是个函数,写在程序里,可以依此来实现mykill。
kill(pid,sig);//进程id和信号id
响应信号
signal(sig,fun);//信号id和处理函数
signal()改变响应方式。
参数为信号id,和处理函数,处理函数分为默认,忽略和自定义。
- 默认 SIG_DFL
- 忽略 SIG_IGN
- 自定义 void fun(int sig)
signal()是回调函数,即自己写而不由自己调用,而是其他人员或内核进行调用。如果重复对一个信号修改响应方式,则只有最后一个响应方式生效。
#include <stdio.h>
#include <stdlib.h>
#include <unsitd.h>
#include <string.h>
#include <signal.h>
void sig_fun(int sig){//这个int的参数会接收到信号的id
printf("sig=%d\n",sig);
signal(sig,SIG_DFL);//修改相应方式
}
int main(){
//当收到SIGINT信号时,调用sig_fun函数,这个函数固定为void返回值,int参数的一个函数。
signal(SIGINT,sig_fun);//程序运行到这并不会调用sig_fun函数,而是声明遇到了SIGINT信号才调用sig_fun函数,函数参数为SIGINT。
//SIGINT为linux的ctrl+c信号,define为2.
while(1){
printf("run");
sleep(1);
}
exit(0);
}
信号处理僵死进程
#include <stdio.h>
#include <stdlib.h>
#include <unsitd.h>
#include <string.h>
#include <signal.h>
void fun(int sig){
printf("%d",sig);
wait(NULL);//子进程给父进程发送17号信号,父进程接收到后调用wait,即可以防止父进程因wait而导致中断等待。
}
int main(){
//signal(SIGCHLD,fun);//SIGCHLD为17号信号
signal(SIGCHLD,SIG_IGN);//linux系统下,忽略也可以处理僵死进程,但仅限linux
pid_t pid =fork();
if(pid==-1){
exit(0);
}
if(pid ==0){
printf("child");
}
else{
sleep(1);
printf("father");
}
}