如皋官方网站建设什么地铁,西安住房和城乡建设部网站,京口区建设局网站,企商网站建设目录标题 信号的一些概念信号的保存pending表block表handler表 信号的捕捉内核态和用户态信号的捕捉 信号的一些概念
1.进程会收到各种各样的信号#xff0c;那么程序对该信号进行实际处理的动作叫做信号的递达。 2.我们之前说过当进程收到信号的时候可能并不会立即处理这个信… 目录标题 信号的一些概念信号的保存pending表block表handler表 信号的捕捉内核态和用户态信号的捕捉 信号的一些概念
1.进程会收到各种各样的信号那么程序对该信号进行实际处理的动作叫做信号的递达。 2.我们之前说过当进程收到信号的时候可能并不会立即处理这个信号而是先将信号进行保存那么我们把信号从产生到抵达之间的状态称为信号未决。 3.进程可以阻塞某个信号这样即使发送信号给对应的进程进程也不会做出任何的处理。 4.被阻塞的信号会一直保持在未决的状态只有当进程解除了对此信号的阻塞时才会执行该信号的动作。 5.阻塞和忽略是两个不同的东西一个信号要是被阻塞说明即使收到了该信号也不会执行该信号的动作而忽略是收到该信号之后根据该信号执行的一个动作只不过该动作是忽略也就是什么都不做而已。
信号的保存
之前的学习中我们知道操作系统是要给每个进程保存收到的信号的而保存的位置就是task_struct中的一个位图这是我们当时说的保存方法但是信号存在三种状态比如说是否收到了信号是否阻塞了信号以及信号与之对应的动作以及动作是否被自定义修改了所以内核使用一张图来保存信号肯定是不够用的所以在实际的应用中使用了三张表一起来保存了信号这三个表分别为pending表block表和handler表那么接下来我们来一一介绍这三个表的功能。
pending表
pinding表本质上就是位图我们之前说进程在运行的任何时候都会收到信号这个信号并不会被立即处理所以进程得保存这个信号保存信号的方式是位图那么这个位图就是pending表位图的比特位位置表示哪个信号比特位的值表示是否收到了该信号如果收到了信号就将对应位置的值修改为1如果没有收到信号对应位置的值就是0. block表
block表也是一个位图位图的位置表示信号编号位图的值表示是否阻塞了对应的信号被阻塞的信号不会被递达只有当阻塞被解除了信号才会被递达即使没有收到对应的信号我们也是可以阻塞一些信号的这里的逻辑可以用下面的伪代码来表示
if(1(signo)pcb-block))
{//信号是被阻塞的不递达
}
else
{if((1(signo-1))pcb-pending){//递达该信号}
}那这里就可以这么来理解操作系统会扫描PCB中的位图来判断是否有信号需要被处理如果信号对应在block上的值为1就直接跳过如果对应在block上的值为0就接续查看对应在panding位图上的值如果值为1的活就执行的对应的方法那么这就是block表的功能。
handler表
handler表是一个函数指针数组每个元素都是一个函数指针数组的位置下标表示信号的编号数组下标对应的内容就表示对应信号的处理方法signal函数可以修改信号对应的方法那么这个本质上就是修改handler表对应元素的指向当信号被递达时就可以根据信号的值来找到handler表中处理信号的方法并执行该方法那么这里的图片就是下面这样
所以看到这里我们可以得出三个结论如果一个信号没有产生的话并不妨碍他可以先被阻塞因为我们只需修改block表中的值跟记录是否收到信号的pending表没有关系如果阻塞的过程中收到了信号那么我们就修改pending位图上对应的值并不会接着去找handler表中的方法 2.进程为什么能够识别信号是因为内核中存在三个表这三个表能让进程认识信号并处理对应的信号。 3.如果SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。
信号的捕捉
信号产生的时候不会立即被处理而是在合适的时候被处理那这个合适的时间是什么时候呢并且我们上面说到操作系统会在某些时刻检查block表和pending表那这个时刻又是指的什么时候呢那么要想解决这些问题我们就得谈谈什么是内核太什么是用户态。
内核态和用户态
我们自己写的代码经过编译和运行之后都是运行在用户态上的但是在我们在编写代码的时候难免会访问到操作系统的资源getpidwaitpid和硬件资源printfreadwrite当我们访问操作系统的资源或者硬件资源的时候我们都得直接或者间接的访问系统提供的借口通过这些接口来访问操作系统的资源我们把这些接口称为系统调用 调用系统调用的时候普通用户不能以自己的身份来调用系统调用必须将自己的身份变为内核态 这就好比当我们是一个公司的基层时副总裁的办公室我们几乎不能直接进出但是当我们的身份变成了公司的CEO时副总裁的办公室我们就可以随意的进出所以当我们的身份变成了内核态时我们才能够有资格使用或者访问内核态提供的代码实际执行系统调用的人是进程但是身份是内核态因为用户态调用内核态的时候要发生身份的转换所以系统调用往往比较费时间一些所以要尽量避免频繁的调用系统调用 但是这里存在一个问题你说进程只是那一个进程但是却存在两个身份那该操作系统是如何来实现这一点的呢那么这里我们就得提到寄存器这个东西
cpu中存在大量的寄存器寄存器分为两种1.可见寄存器2.不可见寄存器虽然寄存器分为两种但是只要和当前进程强相关的寄存器我们都将其称为上下文数据比如说cpu中存在一个寄存器专门用来指向当前进程的pcb这样就可以快速的知道当前运行的进程是哪个再比如说有个寄存器中存储的是当前页表的起始地址这样就可以快速的找到当前进程的页表
cpu中还存在一个名为CR3的寄存器这个寄存器中的内容表征当前进程的运行级别0表示内核态3表示用户态所以想要判断当前的进程是用户态还是内核态就只需要查看CR3里面的内容就行这里存在一个问题我是一个进程进程是怎么跑到内核中执行对应的方法呢之前我们提到的进程地址空间往往指的是用户空间我们写的代码存放或者申请空间都是通过用户级地址空间页表来进行映射这个空间的大小为3GB
这里的页表是用户级页表该页表每个进程都有一份但是地址空间的总大小是4GB用户级空间只占了3GB那剩下的1GB空间用来干什么呢我们把这个1GB的空间称为内核级空间这一块空间也对应了一个页表这个页表就叫内核级页表这个页表的作用就是将操作系统级别的代码从内核虚拟地址空间映射到内存上面
因为操作系统的代码只有一份在机器启动的时候会将这份代码加载到内存上并且每个进程都要使用操作系统代码所以内核级页表只有一份每个进程都通过这个页表来映射找到内存上的操作系统的代码每一个进程都有自己的地址空间用户空间独占内核空间被映射到了每个进程的地址空间的3-4G所以进程要想访问操作系统的接口其实只需要在自己的地址空间上进行跳转即可由用户区跳转到内核区然后再由用户级页表跳转访问内存上的内核代码即可每一个进程的进程地址空间都有3-4GB的内容都会共享一个内核级页表所以无论进程如何切换都不会修改进程地址空间中3-4GB的内容。那用户凭什么能够执行并访问内核的接口或者数据呢原因是当操作系统发现你要访问内核的数据和代码时会帮进程修改CR3寄存器的内容这样就从用户态变成了内核态那么这时就有权利访问内核的数据那操作系统为什么能知道我们要访问内核的数据和代码呢答案是当我们要访问操作系统的接口时最开始还是用户态执行的当以用户态执行系统调用接口时系统调用接口做的最多的事情还是讲寄存器中的3号状态修改成为0号状态也就是将用户态改成内核态然后才跳转区域访问内核的数据和代码那么这就是用户态和内核态的概念
信号的捕捉
我们说操作系统会在合适的时候处理信号那么这个合适的时候就是从内核态返回用户态的时候对信号进行处理那么这就说明我们之前一定先进入了内核态 当执行系统调用和进程切换一个进程没有执行完那一定是放到运行队列或者等待队列那放进队列的时候一定是以操作系统的身份把我放了进去所以得先变成内核态 就会变成内核态
当操作系统进入内核态并且马上要返回普通状态的时候还会做一件事就是检查内核中的三张表 如果block中的信号为0pending中的信号也为0就会接着检查下一个信号当block中的值为1就算pending中的值为1的话也不会执行该信号当block的值为0并且penging的值为1的话就会执行该信号然后就去handler中查找对应的方法处理的方法有默认忽略自定义默认的方法中有70%是终止进程因为当前的身份是内核态所以可以很轻松的终止当前进程忽略就是要处理该信号但是处理的动作是什么都不做所以直接将pending对应位置的信号设置为0就行自定义的方法的实现在用户态那这里就存在一个问题我们能不能以内核态的身份来执行用户的代码呢从技术的角度上我们可以以内核态的身份访问用户的代码但是从设计的角度上来看是不能的因为操作系统不相信任何人handler方法中可能会有对系统造成危险的操作而这些操作操作系统是无法识别是安全还是有害的所以当我们以内核态的身份执行用户的代码的话这部分代码可能会被恶意分子利用然后进行一部分越权的非法动作所以即使从技术上的角度能这么干操作系统也不让你这么干所以当要执行自定义代码的时候操作系统得通过特定的调用将自己的身份从内核态转换称为用户态然后再执行自定义代码那么这个时候自定方法出现了问题就是用户自己的问题了这个时候想干什么坏事情就会收到操作系统的管制了
执行完自定义方法之后也不能直接返回之前调用方法的地方因为我们无法知道当初是从哪里进行跳转的不能从用户态的一个地方跳转到另外一个地方这里必须得有操作系统的支持因为在从用户态跳转到内核态的时候操作系统保存了你的上下文信息所以执行完自定义方法后得从用户态再跳转回内核再通过特定的系统调用由内核态回到用户态上次的地方再继续向后执行 那么这就是信号捕捉的全部流程希望大家能够理解。