jake1036

80x86系统编程之六---------中断和异常

                                   中断和异常处理
    中断和异常是指明系统、处理器、或当前执行程序的某处出现一个事件,该事件需要处理器进行处理。通常,这种事件会导致执行控制被强迫从当前运行的程序转移到被称为中断处理程序或异常处理程序。

 1 异常和中断向量
     每个需要被处理器进行特殊处理的处理器定义的异常和中断条件都被赋予一个表示号,称为向量(vector)。 处理器把赋予异常或中断的向量
     用作中断的处理程序入口地点位置。
       
2 中断描述符表
    该表将每个异常或者中断的向量号同它们的处理过程联系起来。IDT也是由8字节长的描述符组成的一个描述符表。
     指令LIDT和SIDT 指令分别用于加载和保存IDTR寄存器的内容。
 
3 IDT描述符
    

   中断门和陷阱门含有一个长指针(即段选择符和偏移值),处理器用这个长指针把程序执行权转移到代码段中异常或中断的处理过程中。
   任务门描述符中有一个任务TSS段的任务选择符,该任务用于处理异常和中断。

 4 异常和中断处理 
     




      中断描述符中的选择符指向中断处理程序所在段基址,偏移值指向段中的特定的中断处理函数。
      
  5  任务管理
      任务Task 是处理器可以分配调度、执行和挂起的一个工作单元。
      任务切换很像过程调用,但任务切换会保存更多的处理器状态信息。任务切换会把控制权完全转移到一个新的执行环境。这种转移要求保存处理器中几乎 所有的寄存器的当前内容,包括标志寄存器EFLAGS和所有段寄存器。与过程不同,任务不可以重入,任务切换不会把任何信息压入到堆栈中。处理器的状态信息否保存在内存中称为任务状态段的数据结构中。 

 描述符中与任务相关的描述符有两种: 任务状态段描述符 和 任务门 。当执行权传给这两个中的一个的时候,就发生了任务切换。

任务切换很像函数调用,但是任务切换会保存更多的处理器信息,任务切换会把控制权完全转移到一个新的环境中。

 
  5.1 任务的结构和状态
        一个任务由两个部分构成:任务执行空间任务状态段TSS
       任务执行空间包括 代码段 、堆栈段和一个或多个数据段。TSS指定了构成任务执行空间的各个段,并且为任务状态信息提供存储空间。在多任务环境中,TSS也为任务之间的链接提供了处理方法。
       



        一个任务使用指向其TSS的段选择符来指定。当一个任务被加载进处理器中执行时,那么该任务的段选择符、基地址、段限长以及TSS段的描述符就会被加载进
        任务寄存器TR中。如果使用了分页机制,那么任务使用的页目录表基地址就会被加载进控制寄存器CR3中。当前执行任务的状态由处理器中以下所有内容组成:

    5.2  任务的执行
         (1) 使用call指令明确调用一个任务 。
         (2) 使用JMP指令跳转到一个任务(linux使用的方式)。
         (3)隐含调用一个中断句柄处理任务。
         (4)隐含调用一个异常句柄处理任务。

           所有这些调度任务执行的方法都需要一个指向任务门或者任务TSS段的选择符来确定相应的任务。
           当调度一个任务执行时,当前运行的任务 会和被调用的任务会发生任务切换。在任务切换期间,当前正在运行的任务会把执行环境保存到
           它的TSS中,并暂停该任务的执行。此后新调用任务的上下文环境会被加载进内存处理器中,并从加载的EIP处,开始执行新的任务。
         
           如果当前执行的任务调用了被调用的新任务,那么当前执行任务的TSS段选择符 会保存在被调用任务的TSS中。从而提供了一个返回调用者
           的链接。

5.3  任务管理数据结构 
        (1) 任务状态段TSS
        (2) TSS描述符  
        (3) 任务寄存器TR
        (4)任务门描述符
         (5)标志寄存器EFLAGS中的NT标志。
 
 
  5.3.1任务状态段
          用于恢复一个处理器状态的所有信息存储在称为任务状态段(TSS)的数据结构中。
           TSS的结构图如下:
          



      TSS中的各个字段主要包括以下两种:
      (一) 动态字段 : 当任务切换的时候而被挂起的时候,处理器会自动更新字段的内容。  这些字段包括:
             (1) 通用寄存器字段。用于保存 EAX ,ECX ,EDX ,EBX,ESP ,EBP,ESI,EDI。
             (2) 段选择符字段        用于保存ES,CS,SS,DS,FS,GS。
             (3) 标志寄存器EFLAGS  再切换之前保存EFLAGS
             (4) 指令指针EIP 字段,在切换之前保存EIP寄存器的内容。
             (5) 先前任务连接字段,含有前一个任务的TSS段选择符。该字段允许任务使用IRET指令切换到前一个字段。
      (二)静态字段 :处理器会读取静态字段的内容,但是不会改变它们。这些任务内容通常是在任务被创建的时候设置的。 
             (1) LDT段选择符字段。含有任务的LDT段的选择符。
             (2)CR3控制寄存器字段。含有任务使用的页目录物理基地址。 
             (3) 特权级0,1和2的堆栈指针字段。这些堆栈指针由堆栈段选择符(ss0 , ss1 ,ss2) 和栈中偏移指针(esp0 , esp1 ,esp2)组成。对于指定的一个任务,这些字段的值是不会改变的。
             (4)调试陷阱。
             (5)I/O 位图基地址字段。

                
    5.3.2 TSS描述符
             任务状态段TSS也是由段描述符来描述的,TSS描述符只能存放在GDT中。
                     
              类型字段TYPE中的忙标志B用于指明任务是否处于忙状态。忙状态的任务是指当前正在运行的任务或者等待的任务。
              任务是不可以递归执行的,因此处理器使用忙标志B来检测任何对被中断执行的任务的调用。
              其中基地址、段限长、描述符特权级DPL、颗粒度G和存在位具有与数据段描述符中相应字段的同样功能。

               使用调用或者跳转指令,任何可以访问TSS描述符的程序都能够造成任务切换。把TSS描述符加载进任何段寄存器将导致一个异常

    5.3.3 任务寄存器 
             任务寄存器TR中存放着16位的段选择符(可见部分)以及当前任务TSS段的整个描述符(不可见部分)。这些信息是从GDT中当前任务的TSS描述符中复制过来的。
              LTR 和 STR指令分别用于保存和加载TSS描述符中的可见部分,即TSS段的选择符。
              
    5.3.4 任务门描述符
             任务门描述符提供一个任务间接受保护引用。任务门描述符可以存放在 GDT LDT IDT中。
             任务门描述符中的TSS选择符字段指向GDT中的一个TSS段描述符。
   
              

                 
     5.3.5 任务切换 
              处理器可以使用以下4种方式之一执行任务切换操作。
          (1) 当前任务对GDT 中的TSS描述符执行JMP 或 CALL 指令。
          (2) 当前任务对GDT和LDT中的任务门描述符执行JMP和CALL指令。
          (3) 中断或者异常向量指向IDT表中的任务门描述符。
          (4) 当EFLAGS 中的NT标志置位时,当前任务执行IRET指令。
              

                  任务切换示意图 如上 。

5.3.6任务链
      TSS中的前任务链字段以及EFLAGS中的NT标志用于返回到前一个任务操作中。NT标志指出了当前执行的任务是否嵌套在另一个任务中执行,并且当前任务的前一个
      任务链字段中存放着嵌套层中更高层的TSS选择符。
     
    当使用CALL 指令,中断或者异常造成任务切换的时候,处理器把当前TSS段的选择符复制到新任务TSS段的前一任务链字段中,并且在EFLAGS中设置NT标志,NT标志指明TSS的前一任务链字段中存放有保存的TSS段选择符。如果软件使用了IRET指令挂起新任务,处理器就会使用前一任务链中的值和NT标志返回到前一个任务中。
     
注意如果当前任务的切换是由JMP指令造成的,那么新任务就不被嵌套。

  6 任务地址空间 
        任务的地址空间由任务能够访问的段构成。这些段包括代码段、数据段、堆栈段、TSS中引用的系统段以及任务代码段能够访问的任何其他段。
        TSS中的LDT字段可以用于给出每个任务自己的LDT。对于一个给定的任务,通过把任务相关的所有段描述符都放入LDT中,任务的地址空间可以与其他任务
        相隔离。
        如果开启了分页机制,则TSS中的CR3寄存器字段可以让每个任务有它自己的页表。或者,几个任务能够共享相同的页表集。 
         
       (1) 把任务映射到线性和物理地址空间
             所有任务共享一个线性到物理地址空间的映射。当没开启分页机制时,就只能够使用这个方法。当开启了分页机制,就可以让所有的任务使用同一个页目录的方法
             来实现。
       (2)每个任务有自己的线性地址空间,并映射到物理地址空间。
            通过让每个任务使用不同的页目录,我们就可以使用这种映射技术。  
   6.1任务逻辑地址空间
        为了在任务之间共享数据,可以使用以下方法之一来为数据段建立共享的逻辑到物理地址空间的映射:
        (1) 通过使用GDT中的段描述符,所有任务必须能够访问GDT的段描述符,如果GDT中的某些段描述符能够指向线性地址空间的一些段,并且这些段被映射到所有任务共享的物理地址空间中,那么所有的任务都可以共享这些段中的代码和数据。

       (2) 通过共享的LDT。
       (3) 通过映射到线性地址空间公共地址区域的不同LDT中的段描述符。这种方式最好。


  7 保护模式编程初始化 
           
     7.1  进入保护模式时的初始化操作 
           操作系统加载和初始化软件(bootsect.s setup.s 和 head.s) 必须在内存中先设置好保护模式下使用的数据结构的基本信息。
          (1) 保护模式中断描述符表IDT。
          (2) 任务状态段TSS。
          (3) 局部描述符表LDT。
          (4) 若使用分页机制,则起码设置一个页目录和一个页表。
          (5) 处理器切换到保护模式下运行的代码段。
          (6) 含有中断和异常处理程序的代码段。
          (7)全局描述符表GDT         

          在能够切换到保护模式之前,初始化代码还必须设置以下系统寄存器。
           (1) 全局描述符表基地址 寄存器GDTR 。
           (2) 中断描述符表基地址寄存器 IDTR 。
           (3) 控制寄存器CR1- CR3。

            在初始化了这些数据结构、代码模块和系统寄存器之后,通过设置CR0寄存器的保护模式标志位PE(位0),处理器就会切换到保护模式下运行。
          
     7.2 保护模式系统结构表 
           LDT表的段描述符还要求存放在GDT表中。软件初始化代码必须设置一个保护模式IDT,其中最少需要含有处理器可能产生的每个异常向量对应的门描述符,
           在使用IDT之前,必须使用LIDT指令把IDT表的基地址和长度加载到IDTR寄存器中。
           分页机制初始化 : 分页机制由控制寄存器CR0中的PG标志位设置。当设置PG标志之前,必须先初始化以下数据结构和寄存器:
             (1) 软件必须在物理内存中建立至少一个页目录和一个页表。
             (2) 把页目录表的物理基地址加载到CR3寄存器中。
             (3) 处理器处于保护模式下,如果满足其他限制,则PG和PE标志可以同时设置。
     
  多任务初始化:
               在处理器切换到保护模式之后,使用LTR指令将TSS段描述符的选择符加载到任务寄存器TR中,这个指令会把TSS标记成忙状态,但是并不是执行任务切换。
               然后处理器可以使用这个TSS来定位特权级0 , 1 ,2 的堆栈,在保护模式中,软件进行第一次任务切换之前必须首先加载TSS段的选择符,因为任务切换
                会把当前任务状态复制到TSS中。

      7.3 模式切换
            7.3.1 切换到保护模式 
             (1)  禁止中断。使用CLI 指令禁止了可屏蔽硬件中断。
             (2)  LGDT 指令把GDT表的基地址加载进GDTR寄存器。
             (3)  执行在控制寄存器中CR0 ,设置PE标志的MOV CR0指令。
             (4) 在MOV CR0 之后,立刻执行一个远跳转JMP 或远调用CALL指令。
             (5) 若要使用局部描述符表,则执行LLDT指令把LDT段的选择符加载到LDTR寄存器中。
             (6) 执行LTR指令,用初始保护模式任务的段选择符或者可写内存区域的段描述符加载到任务寄存器TR中。
             (7) 执行LIDT指令把保护模式IDT表的基地址和长度加载到IDTR寄存器中。
             (8) 执行STI指令开启可屏蔽硬件中断,并且执行必要的硬件操作开启NMI中断。
            
             7.3.2 换回实地址模式
             若想切换回实地址模式,则可以使用MOV CR0 指令把控制寄存器CR0中的PE标志位清 0 。
            (1) 禁止中断。 
            (2) 如果已经开启分页机制,那么需要执行 
                  把程序的控制转移到对等映射的线性地址处。
                  确保GDT和IDT在对等映射的页面上。
                  清除CR0中断的PG标志 。
                  CR3寄存器设置为0x00,用于刷新新TLB缓冲。 
            (3)  把程序的控制转移到长度为64kb的可读段中。
            (4) 使用特定的值来设置加载SS , DS , ES , FS ,GS 段寄存器。
            (5) 执行LIDT指令来指向在1MB实模式地址范围的实地址模式中断表。
             (6) 清除CR0中的PE标志来切换到实地址模式。
            (7) 执行一个远跳转指令到一个实模式程序中。
             (8) 加载实地址模式程序代码会使用的SS , DS , ES ,FS ,GS。
              (9) STI 开启硬件中断。






















































































































posted on 2010-09-24 16:23 kahn 阅读(808) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理