linux 2.6源代码情景分析笔记之中断与异常2

来源:互联网 发布:服装设计网络班 编辑:程序博客网 时间:2024/06/05 22:54

高级可编程控制器

如果系统只有一个cpu,主pic输出线以直截了当的方式连接到cpu的intr引脚。如果系统中包含两个或多个cpu,就需要更加复杂的pic.
intel从p3开始引入i/o高级可变成控制器(APIC)组件。80x86微处理器当前所有的cpu都含有一个本地apic。每个本地apic都有32位寄存器,一个内部时钟,一个本地定是设备以及为本地apic中断保留的两条额外的irq线lint0和lint1。所有本地apic都连接到一个外部i/o apic,形成一个多apic系统。

i/o apic的组成:一组24条irq线,一张24项的中断定向表(interrupt redirection table),可编程寄存器,以及通过apic总线发送和接收apic信息的一个信息单元,中断优先级并不与引脚号相关联:中断重定向表中的每一项都可以被单独变成以指明中断向量和优先级,目标处理器以及选择处理器的方式。重定向表中的信息用于把每个外部irq 信号转换为一条消息,通过apic总线把消息发送给一个或者多个本地apic单元。

来自外部硬件设备的中断请求以两种方式在可用cpu之间分发:
1静态分发:irq信号传递给重定向表相应项中所列出的本地apic。中断立即传递给一个特定的cpu,或者一组cpu,或者所有cpu.
2动态分发:如果处理器正在执行最低优先级的进程,irq信号就传递给这种处理器的本地apic.

每个本地apic都有一个可编程任务优先级寄存器(task priority register)trr用来计算当前运行进程的优先级。intel希望在操作系统内核中通过每次进程切换对这个寄存器进行修改。
如果两个或者多个cpu共享最低优先级,就利用仲裁技术在这些cpu之间分配负荷。在本地apic的仲裁优先级寄存器中,给每个cpu都分配一个0-15范围内的数值。
每当中断传递给一个cpu时,其相应的仲裁优先级就自动设置为0,而其他每个cpu的仲裁优先级都增加1.当仲裁优先级寄存器大于15时,就把它设置为获胜cpu的前一个仲裁优先级加1.因此,中断以轮转方式在cpu之间分发,且具有相同的任务优先级。
除了在处理器之间分发中断外,多apic系统还允许cpu产生处理器间中断。当一个cpu希望把中断发给另一个cpu时,就在自己本地apic的中断指令寄存器(interrupt command register,ICR)中存放这个中断向量和目标本地 APIC标识符。然后通过apic总线向目标本地apic发送一条消息,从而向自己的cpu发出一个相应的中断。
异常:80x86微处理器发布了大约20种不同的异常。内核必须为每种异常提供一个专门的异常处理程序。

中断向量表(interrupt descriptor table IDT)是一个系统表,与每一个中断或者异常向量相联系,每一个向量在表中有相应的中断或者异常处理程序的入口地址。内核在允许中断发生前,必须是当地初始化IDT。

表中每一项对应一个中断或者异常向量,每个向量由8个字节组成。因此最多需要256*8=2048字节来存放idt.
idtr cpu寄存器使得idt可以位于内存的任何地方,指定idt的线性基地址以及其限制。在允许中断之前,必须用lidt汇编指令初始化idtr.
任务门(task gate):当中断信号发生时,必须取代当前进程的那个进程的tss选择符存放在任务门中。
中断门(interrupt gate):包含段选择符和中断异常处理程序的段内偏移量。当控制权转移到一个适当的段时,处理器清IF标志,从而关闭将来会发生的可屏蔽中断。
陷阱门(tarp gate):与中断门类似,只是控制权传递到一个适当的段时处理器不修改if标志。
linux利用中断门处理中断,利用陷阱门处理异常。

中断和异常的硬件处理:
当执行了一条指令后,es和eip这对寄存器包含下一条将要执行的指令的逻辑地址。在处理那条指令之前,控制单元会检查在运行前一条指令时是否已经发生了一个中断或者异常。如果发生了一个中断或者异常,那么控制单元执行下列操作。
1.确定雨中段或者异常关联的向量i(0<=i<=255)
2.读由idtr寄存器指向的idt表中的每i项
3.从gdtr寄存器获得gdt的基地址,并在gdt中查找,以读取idt表项中的选择符所标识的段描述符。这个描述符指定中断或异常处理程序所在段的基地址。
4.确定中断是由授权的(中断)发生源发出的。首先将当前特权级cpl(存放那个在cs寄存器的低两位)与段描述符(存放在gdt中)的描述符特权级dpl比较,如果cpl小于dpl,就产生一个“general protection”异常,则作进一步的安全检查:比较cpl与
处于idt中的门描述符的dpl,如果dpl小于cpl,就产生一个“general protection”异常。这最后一个检查可以避免用户应用程序访问特殊的陷阱门或者中断门。
5.检查是否发生了特权级的变化,cpl是否不同于所选择的段描述符的dpl。如果是,控制单元必须开始使用与新的特权级相关的栈。
a.读tr寄存器,以访问运行进程的tss段。
b.用与新特权级相关的栈段和栈指针的正确值装载ss和esp寄存器。这些值可以在tss中找到。
c.在新的栈中保存ss和esp以前的数值,这些数值定义了与旧特权级相关的栈的逻辑地址。
6.如果故障已经发生,用引起异常的指令地址装载cs和eip寄存器,从而使得这条指令能再次执行。
7.在栈中保存eflags,cs和eip的内容
8.如果异常产生了一个硬件出错码,则将它保存在栈中。
9.装载cs和eip寄存器,其值分别是idt表中第i项门描述符的段选择符和偏移量字段。这些数值给出了中断或者异常处理程序的第一条指令的逻辑地址。

控制单元所执行的最后一步就是跳转到中断或者异常处理程序。也就是当处理完中断线号后,控制单元所执行的指令就是被选中处理程序的第一条指令。
中断或异常被处理完后,相应的处理程序必须产生一条iret指令,把控制权转给被中断的进程。这将迫使控制单元:

1.用保存在栈中的值装载cs,eip或者eflags寄存器。如果一个硬件出错码曾被压入栈中,并且在eip内容的上面,那么,执行iret指令前必须先弹出这个硬件出错码。
2.检查处理程序的cpl是否等于cs中最低两位的数值(这意味着被中断的进程与处理程序运行在同一特权级)。如果是,iret终止执行;否则转入下一步。
3.从栈中装载ss和esp寄存器,返回到与旧特权级相关的栈。
4.检查ds,es,fs以及gs段寄存器的内容,如果其中一个寄存器包含的选择符是一个段描述符,并且其dpl数值小于cpl,那么,清相应的段寄存器。控制单元这么做是为了禁止用户态的程序(cpl=3)利用内核以前所使用的段寄存器(dpl=0)。如果不清这些寄存器,怀有恶意的用户态程序就可能利用它们来访问内核地址空间。

原创粉丝点击