《自己动手写操作系统》第三章 pmtest9源码解析——中断处理程序

本节,我们将学习现代操作系统的一个重要的特点——中断处理机制。同样,中断处理也是软硬件协同完成的,其中硬件部分:8259A负责可编程中断控制器,通过读写端口,实现中断信号和中断号的绑定等工作;CPU通过门描述符与中断向量表来实现中断服务程序。最后,访问一个中断和访问一个门的过程是类似的,读者可以进行相应比较。本文,主要通过pmtest9讲解中断程序的编写。

中断门的实现包括如下内容:设置8259A,进行中断硬件信息的读写;建立IDT;实现一个中断。

1.预览

保护模式下,中断机制发生了很大的变化。中断向量表被IDT所取代,实模式下能够使用的BIOS中断保护模式下已经不能使用了。IDT,中断描述符表,里面存放着中断、陷阱、任务门,将每一个中断向量和描述符表对应起来。中断门和陷阱门的描述符如下:
《自己动手写操作系统》第三章 pmtest9源码解析——中断处理程序_第1张图片
从中断向量到中断处理程序的对应过程:
《自己动手写操作系统》第三章 pmtest9源码解析——中断处理程序_第2张图片
调用门和中断的处理机制类似,不同的是,前者用call指令,后者用int 指令。另外,linux中没有用到任务门,所以我们不再此做过多讲解。

2.中断和异常机制

两个基本问题:处理器可以相应何种类型的中断?收到中断如何进行处理? 保护模式下的中断和异常介绍可以参考这里:
保护模式下的异常和中断
 

2.1对于异常

注意FAULTTRAPABORT的区别和联系。
 

2.2对于中断

外部中断需要建立中断和向量号之间的联系。外部中断分为不可屏蔽中断和可以屏蔽中断,分别由CPUNMIINTR接收。结构如下图: 可屏蔽中断和CPU的关系是通过设置可编程中断控制器实现的。那么如何将中断请求与中断向量对应起来呢?BIOS在初始化的时候, IR0IR7被设置为对应向量08h0fh,而通过表,我们知道保护模式下的向量号08h0fh已经被占用,我们只能从新设置8259A ,这可以通过向特定的端口写入ICWInitialzation Command Word)来实现的。  

3.编程操作8259A

3.1什么是端口

端口是指接口电路中的一些寄存器,这些寄存器分别用来存放数据信息、控制信息和状态信息,相应的端口分别称为数据端口、控制端口和状态端口。

电脑运行的系统程序,其实就像一个闭合的圆圈,但是电脑是为人服务的,他需要接受一些指令,并且要按照指令调整系统功能来工作,于是系统程序设计者,就把这个圆圈截成好多段,这些线段接口就叫端口(通俗讲是断口,就是中断),系统运行到这些端口时,一看端口是否打开或关闭,如果关闭,就是绳子接通了,系统往下运行,如果端口是打开的,系统就得到命令,有外部数据输入,接受外部数据并执行。

其中,主8259A和从8259A对应的端口地址是20h/21hA0h/A1h


3.2ICW的格式与意义

相关代码:
; Init8259A ---------------------------------------------------------------------------------------------
Init8259A:
    mov al, 011h
    out 020h, al    ; 主8259, ICW1.
    call    io_delay

    out 0A0h, al    ; 从8259, ICW1.
    call    io_delay

    mov al, 020h    ; IRQ0 对应中断向量 0x20
    out 021h, al    ; 主8259, ICW2.
    call    io_delay

    mov al, 028h    ; IRQ8 对应中断向量 0x28
    out 0A1h, al    ; 从8259, ICW2.
    call    io_delay

    mov al, 004h    ; IR2 对应从8259
    out 021h, al    ; 主8259, ICW3.
    call    io_delay

    mov al, 002h    ; 对应主8259的 IR2
    out 0A1h, al    ; 从8259, ICW3.
    call    io_delay

    mov al, 001h
    out 021h, al    ; 主8259, ICW4.
    call    io_delay

    out 0A1h, al    ; 从8259, ICW4.
    call    io_delay
  mov al, 11111110b   ; 仅仅开启定时器中断
    ;mov    al, 11111111b   ; 屏蔽主8259所有中断
    out 021h, al    ; 主8259, OCW1.
    call    io_delay

    mov al, 11111111b   ; 屏蔽从8259所有中断
    out 0A1h, al    ; 从8259, OCW1.
    call    io_delay

    ret
; Init8259A ---------------------------------------------------------------------------------------------

上述代码包括两部分:中断向量的设置部分+中断屏蔽的设置部分。其中,屏蔽的设置部分是通过OCW来完成的。OCW:是operation Control WordOCW1用于设置屏蔽信息,OCW2用于发送EOI信号(end of int),表示中断处理完成,可以接受下一个中断了。OCW的设置是通过21hA1h实现的,结构意义如下:

 4.建立IDT

这一步和设置GDT类;IDT初始化以后,无法从保护模式回到实模式,因为IDTR8259A的内容已经被改变了。注意观察一下,IDTR前后的变化。  

5.时钟中断实验

这里,我们将打开时钟中断IRQ0.修改初始化8259A的代码,使能IRQ0;设定IDT;绑定中断处理函数。开启时钟中断:    mov al, 11111110b   ; 仅仅开启定时器中断
设定IDT:
; IDT
[SECTION .idt]
ALIGN   32
[BITS   32]
LABEL_IDT:
; 门                                目标选择子,            偏移, DCount, 属性
%rep 32
            Gate    SelectorCode32, SpuriousHandler,      0, DA_386IGate
%endrep
.020h:          Gate    SelectorCode32,    ClockHandler,      0, DA_386IGate
%rep 95
            Gate    SelectorCode32, SpuriousHandler,      0, DA_386IGate
%endrep
.080h:          Gate    SelectorCode32,  UserIntHandler,      0, DA_386IGate

IdtLen      equ $ - LABEL_IDT
IdtPtr      dw  IdtLen - 1  ; 段界限
        dd  0       ; 基地址
; END of [SECTION .idt]

绑定中断处理函数:
; int handler ---------------------------------------------------------------
_ClockHandler:
ClockHandler    equ _ClockHandler - $$
    inc byte [gs:((80 * 0 + 70) * 2)]   ; 屏幕第 0 行, 第 70 列。
    mov al, 20h
    out 20h, al             ; 发送 EOI
    iretd
绑定:
    mov al, 020h    ; IRQ0 对应中断向量 0x20
    out 021h, al    ; 主8259, ICW2.
    call    io_delay

开中断:
 int 080h
    xchg    bx,bx
    sti
    jmp $
 

6.其他

中断门和调用门相比,需要入栈的还有Eflags(和error code),堆栈切换情况如下(可能有特权级的切换): 从中断或者异常返回要用iretd,和ret相比,它会改变EFLAGS。另外iretd执行的时候,error code并不会自动从堆栈弹出,需要手动清除。 中断门和陷阱门的区别在于陷阱门产生的中断不会改变IF.

你可能感兴趣的:(中断处理,自己动手写操作系统,8259A编程)