目录
3.信号的处理
3.1信号是什么时候被处理的
read系统调用
3.2信号是怎样被处理的
内核态和用户态
3.3操作系统是如何运行处理信号的呢?
中断技术
什么让操作系统运行起来的
3.4捕捉信号的其他方式
编辑
demo代码
3.信号的处理
3.1信号是什么时候被处理的
信号在发送给进程之后不是立即处理的,而是等到合适的机会,在去处理信号的。
什么是合适的机会呢?
进程从内核态要切换回用户态时,会检测处理信号。
为什要内核态才能处理信号呢?
因为处理信号的时候一定会对内核数据结构做修改,就像在处理信号之前,要把pending表中信号对应的bit位置为0,然后去调用handler函数,处于用户态的时候是不允许让问内核数据的。
这里可能会有个问题,直接使用系统调用不就能对内核数据进行修改吗?还用上面说的来回切换多麻烦啊?
这是因为在执行系统调用的时候,也会切换到内核态的,举个例子。
read系统调用
1.将参数拷贝到内核
2.切换为内核态
3.在task_struct中找到文件描述符表。
4.访问0号fd的文件,将该文件的缓冲区拷贝到buffer中
5.将结果返回给用户。
6.返回用户态
信号的大致处理流程就是这样的。
但是自定义和默认处理方式流程是有区别的。
自定义处理:执行sighandler需要切换回用户态,去执行自定义的函数,然后返回内核态,内核态在返回运行结果给用户态。
默认处理:直接在内核态执行默认的sighandler函数,然后返回用户态即可。
3.2信号是怎样被处理的
内核态和用户态
进程的虚拟地址空间,分为用户区0-3g,和内核区3-4g,一共是4g。
这也就是为什么,进程总能找到操作系统,让操作系统替进程执行一些动作。
操作系统被映射到了,进程的地址空间内,经过mmu+页表将虚拟的地址转化为物理地址,就找到操作系统了。
内核级的页表只有一份,因为操作系统只有一个,操作系统的内核数据也只有一份,只需要一个内核级的页表,建立不同进程的虚拟地址到操作系统所处物理内存的映射,就可以让多个进程看到一个操作系统。
但是进程是有多个的,每个进程的数据也是不同的,这就需要不同的页表,来映射不同的进程。
3.3操作系统是如何运行处理信号的呢?
首先呢操作系统是一个功能强大的傀儡,它没有自己的思想,得有人告诉操作系统要做什,它才会去做,不然操作系统什么都不会做。
中断技术
信号技术本质就是软件中断,软件中断就是对硬件中断的模拟。
硬件中断:键盘直接连接到cpu的针脚上,当键盘输入的时候,会给对应的针脚一个高电频,cpu会拿着这个针脚的编号,去操作系统的中断向量表(函数指针数组),执行对应的函数。
软件中断:由软件产生一个中断信号,然后去操作系统的终端向量表,执行对应的函数。
重谈read方法
cs的寄存器的后两位是权限标识位。00代表是内核态,11代表用户态。
首先判断当前是不是用户态,是就把系统调用表中对应系统调用的编号放入eax寄存器中。
执行int 0x80指令陷入内核态,执行系统调用。
什么让操作系统运行起来的
操作系统是一个死循环,不断的接受其他外部的中断。
由一个硬件,向操作系统发送一个频率非常高,时间非常短的周期时钟中断,操作系统会拿着中断号去操作系统的中断向量表中,执行进程调度,内存管理,更新系统时间。。。任务。
3.4捕捉信号的其他方式
在处理信号的时候,会讲处理的信号屏蔽,防止一直接受同一个信号陷入循环。
参数:
signum是想要捕捉的信号,或者一些选项(不介绍)。
act:act是个结构体,含有一下内容。
sa_handler是想要自定处理的函数
sa_sigaction处理实时信号
sa_mask除了屏蔽当前信号,还要屏蔽哪些信号。
sa_flag是一些选项。
oldact:输出型参数,将原来信号的处理保存到oldact中。
demo代码
使用一下这个接口,屏蔽2号信号的同时屏蔽3,4,5号信号。
看下现象,运行起来的pending位图2,3,4,5的位置应该都是1。
验证
#include<signal.h>
#include<iostream>
#include<unistd.h>
void print(sigset_t &pending)
{
for(int i = 31; i >= 1; i--)
{
if(sigismember(&pending,i))
{
std::cout<<"1";
}
else
{
std::cout<<"0";
}
}
std::cout<<std::endl;
}
void handler(int signo)
{
//获取pending并打印
sigset_t pending;
sigemptyset(&pending);
while(true)
{
sigpending(&pending);//获取pending位图
print(pending);//打印pending
sleep(1);
}
}
int main()
{
struct sigaction act,oact;
act.sa_handler = handler;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);//初始化信号集
sigaddset(&act.sa_mask,3);//在信号集中添加3号信号
sigaddset(&act.sa_mask,4);
sigaddset(&act.sa_mask,5);
sigaction(2,&act,&oact);//捕捉信号
while(true)
sleep(1);
return 0;
}
第一次kill-2让信号被捕捉,第二次就直接被阻塞在pending位图中。
kill -3 -4 -5每次pending位图对应的位置都会变为1.