信号的发送的对象是pcb
task_struct{
int signal; //0000 0000 .... 0001
进程pcb中存在int型的signal来保存信号,用位图的方式,比特位的0,1表示是否收到信号
比特位位置表示信号的编号。
发信号的本质就是修改task_struct的信号位图对应的比特位。
信号的作用是为了给用户层传递信息
信号的保存的原因:进程收到信号不会立即处理这个信号,要有个时间窗口
实时信号:立即处理,不能丢失
信号的保存
信号递达,信号未决,信号阻塞
信号递达(delivery):实际执行信号的处理的动作,即执行。
信号未决(pedding):信号从产生到递达之间的状态,就是把修改对应比特位,即信号传到进程。
进程可以选择阻塞(Block)某个信号,被阻塞的信号将处于pending状态,解除阻塞才进入递达状态。
忽略是递达后的状态和block不一样,忽略和自定义,默认是递达后三种处理方法
用户提供的方法可以覆盖handler数组的自定义方法
信号集类型sigset_t:封装好的位图
sigemptyset():初始化,并类图所有项都为0
sigaddset(sigset_t*set, int signum) 将位图的第signum位设为1
函数sigprocmask读取或者更改进程的信号屏蔽字。
修改当前进程的block位图
how的选项是
SIG_BLOCK:取位图交集
SIG_UNBLOCK:最后的set的block是参数set和old set共有的
SIG_SETMASK :覆盖
set:是新的位图
oldset是系统的位图
接口实操代码
#include<iostream>
#include<signal.h>
#include<unistd.h>
using namespace std;
void PrintPending(sigset_t &pending)
{
for(int signo=1;signo<=31;signo++)
{
if(sigismember(&pending,signo))
{
cout<<"1";
}
else
{
cout<<"0";
}
}
cout<<endl;
}
void handler(int signo)
{
cout<<"catch a signo: "<<signo<<endl;
}
int main()
{
signal(2,handler);
//对栈上的位图进行操作
sigset_t bset,oset; //封装好的类图
sigemptyset(&bset); //初始化类图
sigaddset(&bset,2); //修改本地的类图
//对阻塞的位图进行操作
sigprocmask(SIG_SETMASK,&bset,&oset);
sigset_t pending;
int cnt=0;
while(true)
{
int n=sigpending(&pending); //获取pending类图
if(n<0)continue;
PrintPending(pending);
sleep(1);
cnt++;
if(cnt==20)
{
cout<<"unblock 2 signo"<<endl;
sigprocmask(SIG_SETMASK,&oset,nullptr);
}
}
// 打印pending
// 发送2号信号 000...010
return 0;
}
利用mask可以把所有信号进程屏蔽,信号不就不会被处理。
信号捕捉处理
当我们的进程从内核态回到用户态进行信号的检测和处理
当系统调用时,操作系统会做身份切换的,用户身份变成内核身份,或者反着来
int 80 从用户态变成内核态
内核态:允许访问自己的系统代码和数据。
系统调用就是在1GB的地址空间进行执行的
操作系统视角:任何一个时刻,都会有进程执行,执行操作系统的代码就可以随时执行
操作系统的本质:基于时间中断的死循环,有一个时钟芯片,发送时间中断,驱动操作系统进行调度。
ecs寄存器有两个标志比特位 00 表示内核态,用户态是11,当切换到内核态时,才能访问内核代码。int 80陷入80
do_signal()检测信号
进入到用户态执行方法,如果是系统方法,再进行系统态。