首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

深入理解 x86/x64 的中断体系(4)

深入理解 x86/x64 的中断体系(4)

2.7 使用 trap gate             trap gate 在结构上与 interrupt gate 是完全一样的,参见节 2.6 的那图,trap gate 与 interrupt gate 不同的一点是:使用 trap gate 的,processor 进入 interrupt handler 前并不改变 eflags.IF 标志,这意味着在 interrupt handler 里将允许可屏蔽中断的响应。
             
*(--esp) = eflags;                                               /* push eflags */
if (gate_descriptor.type == TRAP_GATE) {
                                                                  /* skip: do nothing */
} else if (gate_descriptor.type == INTERRUPT_GATE){
         eflags.IF = 0;                                         /* clear eflags.IF */      
} else if (gate_descriptor.type == TASK_GATE) {
         ... ...
}
     
              2.8 使用 task gate              在使用 task gate 的情形下变得异常复杂,你需要为 new task 准备一个 task 信息的 TSS,然而你必须事先要设置好当前的 TSS 块,也就是说,系统中应该有两个 TSS 块:
             
  • current TSS
  • TSS of new task
              当前的 TSS 是系统初始化设置好的,这个 TSS 的作用是:当发生 task 切换时保存当前 processor 的状态信(当前进程的 context 环境),新任务的 TSS 是通过 task gete 进行切换时使用的 TSS 块,这个 TSS 是存放新任务的入口信息。
  
tss_desc        dw 0x67                ; seletor.SI = 3
                dw TSS
                dd 0x00008900
tss_gate_desc   dw 0x67                ; selector.SI = 4
                dw TSS_TASKGATE         
                dd 0x00008900
     
                  在上面的示例代码中,设置了两个 TSS descriptor,一个供系统初始化使用(tss_desc),另一个是为新任务而设置(tss_task_gate),代码中必须设置两个 TSS 块:
  • TSS
  • TSS_TASKGATE
TSS 块的内容是什么在这个示例中无关紧要,然而 TSS_TASKGATE 块中应该设置新任务的入口信息,其中包括:eip 和 cs 值,以后必要的 DS 与 SS 寄存器值,还有 eflags 和 GPRs 值,下面的代码正是做这项工作:
  
; set TSS for task-gate
         mov dword [TSS_TASKGATE+0x20], BP_handler32                ; tss.EIP
         mov dword [TSS_TASKGATE+0x4C], code32_sel                  ; cs
         mov dword [TSS_TASKGATE+0x50], data32_sel                  ; ss
         mov dword [TSS_TASKGATE+0x54], data32_sel                  ; ds
         mov dword [TSS_TASKGATE+0x38], esp                         ; esp
         pushf
         pop eax
         mov dword [TSS_TASKGATE+0x24], eax                         ; eflags     
我将新任务的入口点设为 BP_handler32(),这个是 #BP 断点异常处理程序,保存当前的 eflags 值作为新任务的 eflags 值。
我们必须为 task gate 设置相应的 IDT 表项,正如下面的示例代码:
  
; set IDT vector: It's a  #BP handler
         mov word [IDT+3*8+2], tss_taskgate_sel                  ; tss selector
         mov dword [IDT+3*8+4], 0xe500                           ; type = task gate
     
  示例代码中,我为 vector 3(#BP handler)设置为 task-gate descirptor,当发生 #BP 异常时,就会通过 task-gate 进行任务切换到我们的新任务(BP_handler32)。
   
; load IDT into IDTR
         lidt [IDT_POINTER]

; load TSS
         mov ax, tss_sel
         ltr ax
     
    当然我们应该先设置好 IDT 表和加载当前的 TSS 块,这个 TSS 块就是我们所定义的第1个 TSS descirptor (tss_desc),这个 TSS 块里什么内容都没有,设置它的目的是为切换到新任务时,保存当前任务的 context 环境,以便执行完新任务后切换回到原来的任务。
          
        db 0xcc                         ; throw BreakPoint     
      现在我们就可以测试我们的 BP_handler32(),通过 INT3 指令引发 #BP 异常,这个异常通过 task-gate 进行切换。
      我们的 BP_handler32 代码是这样的:
            
;-----------------------------------------------------
; INT3 BreakPoint handler for 32-bit interrupt gate
;-----------------------------------------------------
BP_handler32:
       jmp do_BP_handler32
BP_msg32    db 'I am a 32-bit breakpoint handler with task-gate on 32-bit proected mode',0
do_BP_handler32:
       mov edi, 10
       mov esi, BP_msg32
       call printmsg
       clts                   ; clear CR0.TS flag
       iret
     
        它只是简单的显示一条信息,在这个 BP_handler32 中,我们应该要清 CR0.TS 标志位,这个标志位是通过 TSS 进行任务切换时,processor 自动设置的,然而 processsor 不会清 CR0.TS 标志位,需要代码中清除。
2.8.1 任务切换的情形在本例中,我们来看看当进行任务切换时发生了什么,processor 会设置一些标志位:
  • 置 CR0.TS = 1
  • 置 eflags.NT = 1
设置 CR0.TS 标志位表示当前发生过任务切换,processor 只会置位,而不会清位,事实上,你应该使用 clts 指令进行清位工作。设置 eflags.NT 标志位表示当前任务是在嵌套层内,它指示当进行中断返回时,需切换回原来的任务,因此,请注意:
当执行 iret 指令时,processor 会检查 eflags.NT 标志是否置位   
当 eflags.NT 被置位时,processor 执行另一个任务切换工作,从 TSS 块的 link 域中取出原来的 TSS selector 从而切换回原来的任务。这不像 ret 指令,它不会检查 eflags.NT 标志位。
processor 也会对 TSS descriptor 做一些设置标志,当进入新任务时,processor 会设置 new task 的 TSS descriptor 为 busy,当切换回原任务时,会置回这个任务的 TSS descriptor 为 available,同时 processor 会检查 TSS 中的 link 域的 TSS selector(原任务的 TSS)是否为 busy,如果不为 busy 则会抛出 #TS 异常。
当然发生切换时 processor 会保存当前的 context 到 current TSS 块中,因此:
  • 切换到目标任务时,processor 会保存当前的任务 context 到 TSS,读取目标任务的 TSS,加载相应的信息,然后进入目标任务
  • 目标任务执行完后,切换回原任务时,processor 会保存目标任务的 context 到目标任务的 TSS 中,从目标任务的 TSS 块读取 link(原任务的 TSS selector),加载相应的信息,返回到原任务
当从目标任务返回时,processor 会清目标任务的 eflags.NT = 0,如前所述目标任务的 TSS descriptor 也会被置为 available
继承事业,薪火相传
返回列表