第07章上 中断和优先级

操作系统是中断驱动的.

中断的本质是产生一个中断信号后,调用相应的中断处理程序.

1 中断分类

把中断按照事件源来分类:来自cpu外部的中断成为外部中断,来自cpu内部的中断称为内部中断.

其中外部中断又可以按照是否会导致宕机来划分:可屏蔽中断和不可屏蔽中断两种.

内部中断按照中断是否正常来划分:软终端和异常

1.1 外部中断

外部中断是指来自cpu外部的中断,中断源必须是某个硬件.所以外部中断又称为硬件中断.

cpu为硬件提供了两条信号线.外部硬件的中断都通过这两条信号线通知cpu,这两条信号线就是INTR和NMI.这两条信号线存在的意义是,任何任务都有轻重缓急,有的中断发生以后,计算机需要立即处理.有的中断,即使发生,也可以不处理.

因此使用两条信号线来区分这两种中断,从INTR引脚接受的中断都是不影响系统运行的,可以随时处理.而NMI引脚接受的中断,基本上会导致宕机,cpu没有再运行下去的必要了

INTR引脚接收到的中断是可屏蔽中断,外部设备包括硬盘,网卡等.可屏蔽中断的意思是,某些外部设备发出中断cpu可以不用理会,因为他们不会让系统宕机.所以可以屏蔽,flags寄存器的if位,来屏蔽这些中断.

linux把可屏蔽中断又分为,上半部分和下半部分,其含义是:希望cpu响应中断的时间越短越好(就是处理一个中断的时间),但是中断程序需要完整的执行.因此将中断处理程序分为上半部分和下半部分,上半部分需要立即执行的,通常只完成中断应答,或是硬件复位,从硬件复制数据等紧迫工作,这部分工作不能再被中断,因此是在关中断的情况下完成(也就是在执行这部分处理程序的时候,屏蔽了外部中断),而那些并不紧急的部分被推迟到下半部分去完成.这部分并不紧迫,所以是在开中断的情况下执行.

NMI引脚进入的中断,表示系统中发生了致命错误,同时表明计算机运行应该终止,主要由三种常见的情况:

扫描二维码关注公众号,回复: 1284825 查看本文章
  1. 内存读写错误
  2. 内存奇偶校验错误
  3. 断电

eflagsif位对不可屏蔽中断无效.

cpu收到中断后,通过中断向量表或是中断描述符表来查找对应的处理程序,因此每个中断需要有一个中断向量号,唯一的定位一个中断.相应的中断想良好通过NMI或是INTR引脚进入cpu

可屏蔽中断不会导致致命的问题,它的数量有限,所以么一种中断可以获得一个中断向量号.不可屏蔽中断引起的致命错误原因很多,每一种都是硬伤,出现以后软件几乎解决不了,多数属于物理上的问题.所以为导致宕机的各种原因分配了一个中断向量号2.

1.2 内部中断

内部中断可分为软中断和异常.

软中断值得是由软件主动发的中断,来自与软件,是软件运行时候主动发起的,是主观上的,不是某种客观上的内部错误.

软中断可以通过中断指令发起:

  1. int 8位立即数:
  2. int3:调试断电指令,其所触发的中断向量号是3.gdb的调试程序,实际上是调试器fork了一个子进程,断点处,父进程修改了子进程的指令,用int3将原有指令替换,从而子进程可以除法中断.
  3. into中断溢出指令,中断向量号是4,
  4. bound检查数组引起越界指令,中断向量号是5.当执行bound指令是,若下表处于数组索引范围之后是,就会除法5号中断
  5. ud2未定义指令,中断向量号是6,当cpu无法识别指令的时候触发

以上的集中常用的软中断指令,除了第一种,其他的又可以成为异常,因此2~4号又有一种错误的意思

异常是另一种内部中断,是指令执行期间cpu内部产生的错误引起的.因为是cpu的错误,因此不受eflagsif位影响.

异常根据错误的轻重程度分为:

  1. Fault故障:是可以被修复的一种,当发生此类异常的时候,cpu将机器状态恢复到异常状态之前,之后调用中断处理程序,返回是,重新执行导致fault故障的那条指令.例如缺页故障
  2. Trap陷阱:通常用于调试,int3,cpu将中断处理程序的返回地址指向导致异常指令的下一跳指令
  3. Ablort终止:无法修复.

某些异常在产生的时候,还会产生一个错误码(部分异常有,部分没有),该中断异常码由硬件自动压入栈中.

2 中断描述符表

中断描述符表IDT,是在保护模式下,用于存储中断处理程序入口的表,当cpu接受一个中断是,需要用中断向量在IDT中索引对应的表项,该表项中由中断处理程序的起始地址,然后去执行中断处理程序.

中断描述符表中所有的表项,都记录一段程序的起始地址(全局描述符表表项中存的是一个基地址,相当于段基址),相当于通向某段程序的大门,因此中段描述符表中的表项,又称为门.

共有四种门:

  1. 任务门:配合TSS是Intel在硬件上提供的一种任务切换机制.可以再GDT,LDT或IDT中,使用call或是jmp.
  2. 中断门包含中断处理程序所在的段的选择子和段内偏移地址,当通过中断门进入中断的时候,会将if位设置为0,屏蔽中断,避免中断的嵌套,linux中用来实现系统调用,只能在IDT中.使用int指令实现从低特权级想高特权级的转移
  3. 陷阱门:同上,但是不会关中断.并且只能存在与IDT中,int3
  4. 调用门:提供给用户进入0特权级的方式.可以在GDT和LDT中,通过call接受门选择子作为参数,实现从低特权级向高特权级转移.jmp只能平级转移

2.1 中断描述符表和中断向量表

区别在于:

  1. 中断描述符表所在的内存地址不限,
  2. 中断描述符表的每个描述符有8个字节

中断描述符表和GDT一样,由一个专门的寄存器保存其地址:IDTR,其值的方式也和GDT一样,指令为lidt 48位

3 特权级

特权级按照权利的大小分为0,1,2,3四级,数字越小,权力越大.

0特权级是操作系统内核所在的特权级,计算机启动的时候以0特权级运行,

3.1 TSS

TSS是Task State Segment是任务状态段,是处理器在硬件上原生支持的一种多任务的切换方式,TSS是一种数据结构.
TSS结构一共104字节,这只是其最少尺寸.

每个任务都有一个TSS结构,它用于一个任务的识别,相当于一个任务的身份证,程序拥有此结构才能运行,是处理器硬件上用于任务管理的结构.处理器能够识别其中的每一个字段.

首先解释:三对espss

由于内核处于0特权级,用户程序处于3特权级,所以一个任务按照特权级来划分是指上被分成3特权级和0特权级两部分.加在一起才是能够让处理器运行的程序.因此完整的任务需要尽力这两种特权级的转换,例如我们使用fork的时候,会进行特权级的转换,因此新建一个进程的操作实际上需要在内核态,也就是0特权级执行.

任务在特权级变换的时候,本质上是处理器的特权级变换.处理器规定,在不同特权级下,使用不同特权级的栈.因为不同特权级使用同一个栈会造成数据混乱.所以3组特权级的栈.

特权级转移分为两类:有中断们\调用们等手段从地特权级向搞特权级转换.用调用返回指令从高特权级返回到低特权级(这是降权的唯一方法)

当从低特权级向高特权级转换的时候,需要将低特权级的栈保存,同时从TSS中取出高特权级的栈.而低特权级的栈,保存在高特权级的栈中.这样,就只需要一个地方保存高特权级的栈就行了,因此TSS中由3组栈寄存器,分别用来保存2,1,0这三个高特权级的栈.而3这个特权级的栈不需要保存,因为他是最低特权级了,不能在返回,只能提权.

3.2 cpl,dpl和rpl

rpl:选择子结构中:权限称为rpl,是请求特权级,是代码段,cs寄存器中的段选择子的rpl,而代码段寄存器中的rpl又成为处理器的当全体特权级.

DPL:描述符特权级,是当前运行的代码所在的代码段的特权级,或是使用的数据所在数据段的特权级

RPL是段选择子的特权级,而DPL是选择子在全局段描述表中索引的表项中的特权级

CPL是,当前cs寄存器中段选择子的RPL,也是正在运行代码所在代码段的DPL

段描述符,指向了一块内存区域(虚拟内存),所以DPL是指定一个内存区域的访问权限.访问者不允许访问比择机特权级更高的资源,并且:

  1. 对于数据段:要求访问者的当前特权级CPL,等级上大于等于数据段的DPL,高级别可以访问低级别的内存
  2. 对于代码段:要求访问者的单钱特权级等于DPL时,才能访问,也就是说只能平级访问

特权级检查只发生在访问的一瞬间,检查过后,在该段上执行的操作不会再被检查.

3.3 门与RPL

RPL主要是为了解决系统调用时,越权的问题.

处理器只有通过门结构才能由低特权级向高特权级转移.

除了任务门,其他三个门结构,都对应一段程序的入口(而不是一个段基址)

门是用来提升特权级的:

门是访问者特权级的下限,要访问门,访问者的特权级CPL要高于门的DPL,然后门中指向的代码的DPL,权限上要高于CPL.否则也就不需要使用门来提权了.

3.4 调用门

调用门,门描述符中记录的是内核服务程序所在的代码段的段选择子以及在代码段中的偏移地址,该段立成只能在0特权级下可以使用可以再GDT以及LDT中.一般通过call或是jmp跳转.

不同的特权级使用不同的栈,而门对应的是一段内核例程(就是一个函数),其参数的传递过程是这样的:参数最初是由用户程序以压栈的形式交给调用门.也就是说参数保存在3特权级下的栈中,处理器在硬件上实现了参数的自动复制,将用户进程压在3特权级栈中的参数自动复制到0特权级栈中.而调用门中有一个参数个数,就是为cpu在赋值参数的时候准备的.参数位数位5,因此最多31个参数.

3.4.1 调用门调用过程

假设用于进程需要调用某个调用门,该调用门中参数个数位2,因此用户进程在call 调用门选择子 之前需要下压栈两个参数(32位)

  1. 压栈两个参数,call 调用门选择子
  2. 根据门描述符中对应的目标代码段的DPL(就是门描述符在GDT索引出门描述符,门描述符中的选择子索引出的段描述符的DPL),确定未来的CPL,假设是0,那么cpu自动从TSS中取出0特权级的ss_new,和esp_new,
  3. 检查新的段选择子对应的描述符的DPL,和TYPE位.
  4. 如果转以后的目标代码段(内核例程所在的内存)的DPL比CPL高,说明需要进行不同特权级的栈的转换.(下面都假设需要转换)
  5. 并加载到ss和esp中,紧接着将旧的ss_old和esp_old压到新的栈中.(这里一定是临时保存了ss_old和esp_old)
  6. 根据调用门描述符中的参数个数,从ss_old栈中,复制对应个数的参数到ss_new中.
  7. 调用门描述符中记录了目标代码段的段选择子和偏移地址,因此需要重新加载cs寄存器(无论和之前cs中的是否一样,都会被加载),因为之前运行的代码一直是用户程序的代码,因此加载之前先将cs_old和eip_old压栈,保存起来,(在高特权级的栈中).这两个是用来恢复用户程序进程的关键.
  8. 当门描述符中的代码段选择子被加载到cs寄存器,偏移量放入eip寄存器以后,就实现了特权级由3到0的转换,开始执行对应的内核服务程序.

内核例程是一个函数,其中结尾是retf用来返回的:

  1. 当处理其执行到retf指令后,知道要元返回,从当前栈中取出cs_old和eip_old.这时候做特权级转换,检查段选择子,根据RPL与检查是否需要变换栈
  2. 然后将cs_old和eip_old加载到cs和eip寄存器中.
  3. 然后,retf后面有参数,那么就增加esp_new的值,跳过栈中的参数,相当于清理栈空间.其参数是调用门参数个数*参数大小
  4. 前面判断需要变换特权级,因此在弹出ss_old和eip_old更换栈

如果在返回是更换了特权级,那么cpu会检查ds,es,fs,gs中的段选择子,如果其rpl大于恢复后的cpl,那么段选择子被置0,因为在内核处理程序中可能使用0特权级才能使用的数据段,而恢复到用户态后,这些段寄存器如果还保存这之前的段选择子,那么用户程序就能访问高特权级中的数据.

这也就是为什么GDT总第0个表项,是全0的,当cpu使用该段的时候,就会引发异常.

注意上面调用门中RPL只是用来判断是否更换特权级,并没有进行其他的检查.

假设一个调用门,起作用是从硬盘复制数据到用户空间,因此参数需要是:数据在硬盘中的地址,要复制到的内存空间,和复制的扇区数.那么当用户提供的目的地的段选择子是合法的,那么应该是一个用户空间的地址,那么进入0特权级以后,cpu可加载该段选择子,但是如果当用户提供的是一个构造出来的处于内核态内存空间的段选择子,那么0特权级下亦然可以加载上,因此就破坏了内存空间.所以单纯的只靠CPL和DPL来进行保护,是不够的.

因此加上RPL就可以解决这个问题:在将段选择子加载到段寄存器时,,cpu会将参数段选择子中的RPL使用CPL替换.这样在检查特权级的时候,需要检查参数的选择子的RPL权限上应该要大于等于段选择子指向段描述符的DPL.当mov ds,ax时,需要保证,ax中的段选择子的RPL>ax中段选择子指向的段描述符的DPL,同时,当前的CPL也>DPL(权限上).

  1. 非一致性代码段:CPL=DPL=RPL
  2. 一致性代码段:CPL=>DPL,RPL=>DPL(权限上)
  3. 数据段:CPL<=DPL,RPL<=DPL(权限上)

其实调用门的时候涉及到两个DPL,第一个DPL是调用门描述符本身的DPL_GATE,第二个DPL是调用门描述符指向的那个段描述符的DPL_CODE.因此是要求,RPL和CPL在DPL_GATE和DPL_CODE之间.类似与跳板.

调用们描述符一般用在:

  1. 调用门是在汇编语言下使用的
  2. 调用们是用来实现系统调用的,但是为了兼容等问题,操作系统很少使用调用门实现系统调用.
  3. 调用们一般在多端模型下使用,大多数情况下需要为调用门指定段选择子.二操作系统为了方便已经采用平坦模型.所以用户进程共享段选择子,用户进程不需要在提供段选择子,所以调用门可以用中断们来代替

4 中断处理过程以及保护

cpu外,外部设备的中断由中断代理芯片接受,处理后将该终端的中断向量号发送给cpu.

cpu内,cpu执行该中断向量好对应的中断处理程序

处理器根据中断向量号定位中断门描述符,中选向量号是中断描述符的索引,当处理器接收到一个外部中断向量号后,他用词中断向量号在中段描述符表中索引对应的中断描述符,然后去执行该中断描述符中指向的中断处理程序.

由于中断通过中断向量号通知处理器的,中断向量号是一个整数,没有RPL,所以在由中断引起的特权级转移的时候不涉及搭配RPL.中断特权检察和调用门类似.对于软件主动发起的软中断,当前特权级cpl必须在门描述符DPL和门描述符指向的代码段的DPL之间.如果中断由外部设备和异常引起的,只检查CPL和门描述符指向的代码段的DPL.

执行中断处理程序.

中断发生后,eflags的NT和TF位被设置为0,当是由中断门引起的中断,if位也被设置位0,避免了中断的嵌套.但是调用门和陷阱允许中断嵌套.

中断返回指令时iret.

猜你喜欢

转载自www.cnblogs.com/perfy576/p/9119215.html