目录
信号的发出及响应
发送信号:即通知进程产生了某种事件。比如中断信号SIGINT(#define SIGINT 2),键盘按下ctrl+c(结束进程),会产该信号。(程序出错退出,大部分情况是由于收到某个信号导致的。)
1.对信号做出响应 signal
(1)调用signal()函数
参数1为信号代号,参数2为函数指针(指针指向返回值为void,参数为int的回调函数) 实质:收到信号signum代号即调用参数2指向的函数。
(2)3种响应方式
对于收到信号的响应方式有三种:默认(SIG_DFL)、忽略(SIG_IGN)、自定义(自己写的信号处理函数)。
#define SIG_DFL (void(*)(int))0 //默认(本质仍为函数指针,将0强转为函数指针)
#define SIG_IGN (void(*)(int))1 //忽略(本质仍为函数指针,将1强转为函数指针)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void fun_sig(int sig)
{
printf("sig=%d\n",sig);
signal(sig,SIG_DFL);//设置收到信号SIGINT采取默认方式响应(ctrl+c~结束进程)
}
int main()
{
//收到信号SIGINT,转到内核态传参调用回调函数(等价于fun_sig(SINGINT))。执行完后返回继续执行while循环。
signal(SIGINT,fun_sig);
//signal(SIGINT,SIG_IGN);//收到信号SIGINT采取忽略的方式进行响应
while(1)
{
printf("main run\n");
sleep(1);
}
//ctrl+\可以结束进程 相当于exit(1);
}
避免忽略所有信号,规定收到信号:9(kill -9),就会结束进程(杀死进程)!!!
2.信号的发出 kill
kill -9+进程号:发出9号信号,强制杀死进程。 使用kill+进程号杀死进程,默认发送的是信号15!
(1)kill()发送信号
kill:发送信号! int kill(pid_t pid,int sig); 返回0代表成功,-1代表失败。
参数1:pid为进程ID号(可以使用ps -ef | grep main 过滤查找正在运行的main进程 )
参数2:sig为给该进程发送的信号。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
int main(int argc,char*argv[]) //argv[0]="./mykill",argv[1]="pid",argv[2]=信号代号
{
if(argc!=3)
{
printf("main arg error\n");
exit(1);
}
int pid=0;
int sig=0;
sscanf(argv[1],"%d",&pid);//ssanf()字符串转整型存储到变量pid中
sig=atoi(argv[2]);
if(kill(pid,sig)==-1)
{
printf("kill error\n");
}
exit(0);
}
(2)辅助知识:字符串转整型sscanf
sscanf() 将参数1字符串转为整型(参数二"%d")存储下来(存储在普通变量中应该加取地址符&):
int a=atoi(字符串);//字符串传参调用atoi()转为整型赋值给a。
3. 父子进程间的信号|17
(1)子进程结束,内核默认给父进程发送17信号。
#include<stdio.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>
#include<signal.h>
void fun(int sig)
{
printf("sig=%d\n",sig);//收到信号SIGCHLD传参调用该函数~代表子进程已结束,变为僵死进程
int val=0;
int id=wait(&val);//将子进程退出码写入到val变量中,wait()函数返回值为捕获到的子进程pid
printf("child id=%d,exit code=%d\n",id,WEXITSTATUS(val));//WEXITSTATUS()获取子进程退出码
}
int main()
{
signal(SIGCHLD,fun);//子进程结束后,内核默认给子进程发送信号SIGCHLD
int n=0;
char* s=NULL;
pid_t pid=fork();
if(pid==-1)
{
exit(1);
}
if(pid==0)
{
n=3;//子进程先结束
s="child";
}
else
{
n=7;
s="parent";
}
for(int i=0;i<n;i++)
{
printf("s=%s\n",s);
sleep(1);
}
exit(0);
}
(2)收到信号处理僵死进程
收到17信号代表子进程结束,子进程先结束会变为僵死进程,正好可以在这时处理僵死进程:调用wait()处理僵死进程+获取退出码,调用WEXITSTATUS()打印退出码。
调用wait(),成功返回值为子进程的id号。
int val=0;
int id=wait(&val); //将退出码存在val中
不能直接打印val,val>>8=WEXITSTATUS(val); //获取子进程退出码
或者可以不用获取打印子进程退出码,只需要处理僵死进程,可以直接wait(NULL);
二、发送信号的实质
PCB进程控制块的本质为结构体struct task_task{},其中有一个成员long signal;
kill(pid,SIGINT); //通过pid可以唯一确定PCB,发送信号2会将结构体中变量signal的第二位(由低位开始数)由0置为1(因此信号值不会超过长整型的位数32)。