什么是进程信号?
进程信号是一种事件通知机制,属于软件中断
信号的作用:发生某事件时,打断进程当前操作,转而去处理这个事件
通俗点的栗子:
假设你正在学习,没有突发事件你不会停止学习。(此时你就是一个进程)此时你的母上大人做好了午饭,跑来叫你吃饭(吃饭信号)。然后你就去吃饭了,不学习了,吃完饭又回来学习(处理完信号回来继续作业)
请注意!信号与信号量完全不是同一个概念!
信号量是进程间通信(IPC)的方式之一,而信号是一种事件通知机制
信号的种类
在linux中使用kill -l
可以查看所有信号,如下(共有62种):
在上图中的信号中:
1-31号信号属于非可靠信号
34-64号信号属于可靠信号
有小伙伴不清楚什么是可靠信号/不可靠信号,可以移步此处详细了解下:可靠信号与不可靠信号
信号的产生
产生方式 | 列举 | 解释 |
---|---|---|
硬件产生 | ctrl+c | 中断,即2号信号SIGINT |
ctrl+\ | 退出,即3号信号SIGQUIT | |
ctrl+z | 中止,即20号信号SIGTSTP | |
软件产生 | kill 命令 | kill -signum pid :给pid号进程发送序号为signum的信号,默认15号终止信号 |
kill() 接口 | 发送指定信号 | |
raise() 接口 | 给调用进程本身发送一个指定信号 | |
alarm() 接口 | 设置一个定时器 | |
abort() 接口 | 给进城发送一个SIGABRT信号 |
信号的生命周期:产生->注册->注销->处理 + 阻塞
之所以先注销再处理的原因:防止信号被重复处理
信号的注册
在未决信号集合中标记信号+添加信号的信息节点
在pcb中有个未决信号集合(还没有被处理的信号的集合)【使用位图实现】
非可靠信号:若信号已经注册,则不作任何操作
可靠信号:无论信号是否注册,都会进行注册
信号的注销
在pcb中删除信号信息节点,重置位图
非可靠信号:删除节点信息,直接位图重置
可靠信号:删除信息节点之后,确定没有相同节点才会重置位图
信号的处理
即信号的递达——执行信号的处理回调函数
sighandler_t signal(int signum,sighandler_t handler);
signum:信号值 handler:信号要新指定的处理方式
handler:SIG_DEL-默认; SIG_IGN-忽略;自定义
类型->typedef void*(sighandler_t(int);
返回值:成功返回信号原来的处理方式;失败返回-1(SIG_ERR)
信号的阻塞
阻塞一个信号表示收到这个信号后暂时不处理,直到解除阻塞之后进行处理
在pcb中,有一个信号阻塞集合,在这个集合中标记哪个信号则表示阻塞哪个信号
int sigprocmask(int how,sigset_t *set,sigset_t *oldset);
how:要对信号阻塞集合进行的操作类型
SIG_BLOCK:将set集合中的信号添加到阻塞集合中
SIG_UNBLOCK:从阻塞集合中移出set集合中的信号
SIG_SETMAS:将阻塞集合中的信号设置为阻塞集合的信号
oldset:用于保存修改前阻塞集合的信息,不使用置空即可
返回值:成功0,失败-1
int sigemptyset(sigset* set);清空set集合
int sigfillset(sigset* set);将所有信号添加进set集合
int sigaddset(sigset* set,int signum);将指定信号添加至set集合
int sigdelset(sigset* set,int signum);从集合中移除指定信号
int sigmember(sigset* set,int signum);判断信号是否在集合中
在所有信号中,有两个信号比较特殊:
SIGKILL-9/SIGSTOP-19不会被阻塞,处理方式也不会被修改
例
1.使用sigprocmask()
阻塞2号信号和40号信号, 分别给进程发送5次2号信号和5次40号信号,观察结果
code
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void signo(int signo){
printf("接收到[%d]信号\n",signo);
}
int main(){
sigset_t set;//定义信号集合
sigemptyset(&set);//清空信号集合
sigaddset(&set,2);//将2号信号加入集合
sigaddset(&set,40);
sigprocmask(SIG_BLOCK,&set,NULL);//将信号集合加入阻塞集合
signal(2,signo); //设置信号2的处理方式
signal(40,signo);
int cnt = 5;
while(cnt--) //kill()产生信号
kill(getpid(),2);
cnt = 5;
while(cnt--)
kill(getpid(),40);
sigprocmask(SIG_UNBLOCK,&set,NULL);//将信号集合移出阻塞集合
return 0;
}
由结果我们可以轻易验证可靠信号与不可靠信号在注册部分的差异:
a.可靠信号产生几次,注册几次
b.不可靠信号,未决集合中存在,只会注册一次,产生再多也只会处理一次
2.使用signal函数自定义SIGINT信号的处理方式
code
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
void sigcb(int signo){
printf("接收到[%d]号信号!\n",signo);
}
int main(){
signal(2,sigcb);
while(1){
printf("main runing!\n");
sleep(1);
}
return 0;
}