1.信号在内核中的表示
因为是三种内核的数据结构,操作系统就可以通过操作这些数据结构提供接口。
如果一个信号没有产生,并不妨碍它可以先被阻塞。
当同一时刻发送大量相同信号时,会丢失。
2.信号的捕捉(重点)
信号产生的时候,不会被立即处理,而是在合适的时候,暂存在pending位图,内核态返回用户态的时候,进行处理。
补充:平时我们所写的代码在编译运行之后全部是运行在用户态的,避免不了访问<操作系统自身的资源[getpid\waitpid...]<硬件资源[printf\write\read];为了访问这些资源,必须通过系统调用。普通用户无法在用户态来执行系统调用,必须让自己的身份变成内核态。实际执行操作系统的"人"是你这个进程,身份其实是内核,往往系统调用比较费时间,尽量避免频繁调用系统调用,STL中的空间配置器会额外申请空间避免频繁向系统申请。
寄存器 可以被划分为两类:<可见寄存器(eabcx,pc指针,程序计数器,ebp(栈顶),exp(栈底)) <不可见寄存器(状态寄存器-是否溢出)。凡是和当前进程强相关的,保存这个进程的相关数据(上下文数据)。有一些寄存器corrector当前正在运行进程的PCB(task_struct),CPU中也有一些寄存器指向页表的起始地址,集成MMU内存单元也能完成虚拟地址向物理内存的转化,CR3寄存器:表征当前进程的状态(为0:内核态,为3:用户态)。查看CPU中CR3寄存器就可以知道当前进程是内核态还是用户态。
进程怎么调用操作系统?
为了进程的独立性每个进程都有自己的代码和数据被放到了物理内存的不同区域;CPU中有一个寄存器指向内核级页表;当开机时,将操作系统加载进物理内存;每一个进程都有自己的地址空间(用户空间独占),内核空间(被映射到了每一个进程的3~4G空间中),进程要访问OS的接口,其实只需要在自己的地址上进行跳转就可以了。无论进程如何切换,3~4G的内核空间都是不变的。
用户凭什么能够执行访问内核的接口或者数据呢?必须更改当前进程的运行级别,系统调用接口起始的位置会把你的用户权限从3号状态改为0号状态,所以系统调用接口前半部分其实是以用户态运行的(int 80陷入内核-更改寄存器状态转换/跳转 页表切换)。总的来说进程如何调用系统调用?其实就是在每个进程的虚拟地址空间中存在3~4G的内核空间,当进行系统调用时就会跳转到内核空间,状态由用户态切成内核态执行系统调用。
系统调用、进程切换、异常都会触发用户态内核态切换。
如果并没有发生系统调用或者进程切换...这些与处理信号没什么相关性,发生内核陷入,状态转化。完成相关调用时,不会立即切回用户态,会去处理PCB中信号(block\pending\handler),在状态寄存器中由0置3
信号捕捉过程涉及的函数:
sigset_t 信号集 ,位图结构,分为两种,block信号集(信号屏蔽字)、pending信号集,表示每个信号的状态,软件比特位为0/1、在硬件的表示就是高低电频、
sigprocmask,更改进程的pending表
cpp
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1
//how有以下三种:追加/解除/重置
//set与how强相关,输入型参数,oset输出型参数
**sigpending,**如果哪个进程调用sigpending,我们就获取哪一个进程的pending位图
cpp
#include <signal.h>
sigpending()
读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。 下面用刚学的几个函数做个实验。