操作系统实现-异常

shilinkun
2022-05-13 / 1 评论 / 648 阅读 / 正在检测是否收录...
博客网址:www.shicoder.top
微信:kj11011029
欢迎加群聊天 :452380935

这一次我们来对异常进行说明

下面是异常和中断号,我们知道一共有256个中断号,其中0-31,一共32个是异常,本次就来实现异常,同样,其实异常就是函数。

image-20220513182650039

我们修改下handler.asm如下

[bits 32]
; 中断处理函数入口 

extern handler_table

section .text

%macro INTERRUPT_HANDLER 2
interrupt_handler_%1:
%ifn %2
    push 0x20222202
%endif
    push %1; 压入中断向量号,跳转到中断入口
    jmp interrupt_entry
%endmacro

interrupt_entry:

    mov eax, [esp]

    ; 调用中断处理函数,handler_table 中存储了中断处理函数的指针
    call [handler_table + eax * 4]
    ; 对应 push %1,调用结束恢复栈
    add esp, 8
    iret

INTERRUPT_HANDLER 0x00, 0; divide by zero
INTERRUPT_HANDLER 0x01, 0; debug
INTERRUPT_HANDLER 0x02, 0; non maskable interrupt
INTERRUPT_HANDLER 0x03, 0; breakpoint

INTERRUPT_HANDLER 0x04, 0; overflow
INTERRUPT_HANDLER 0x05, 0; bound range exceeded
INTERRUPT_HANDLER 0x06, 0; invalid opcode
INTERRUPT_HANDLER 0x07, 0; device not avilable

INTERRUPT_HANDLER 0x08, 1; double fault
INTERRUPT_HANDLER 0x09, 0; coprocessor segment overrun
INTERRUPT_HANDLER 0x0a, 1; invalid TSS
INTERRUPT_HANDLER 0x0b, 1; segment not present

INTERRUPT_HANDLER 0x0c, 1; stack segment fault
INTERRUPT_HANDLER 0x0d, 1; general protection fault
INTERRUPT_HANDLER 0x0e, 1; page fault
INTERRUPT_HANDLER 0x0f, 0; reserved

INTERRUPT_HANDLER 0x10, 0; x87 floating point exception
INTERRUPT_HANDLER 0x11, 1; alignment check
INTERRUPT_HANDLER 0x12, 0; machine check
INTERRUPT_HANDLER 0x13, 0; SIMD Floating - Point Exception

INTERRUPT_HANDLER 0x14, 0; Virtualization Exception
INTERRUPT_HANDLER 0x15, 1; Control Protection Exception
INTERRUPT_HANDLER 0x16, 0; reserved
INTERRUPT_HANDLER 0x17, 0; reserved

INTERRUPT_HANDLER 0x18, 0; reserved
INTERRUPT_HANDLER 0x19, 0; reserved
INTERRUPT_HANDLER 0x1a, 0; reserved
INTERRUPT_HANDLER 0x1b, 0; reserved

INTERRUPT_HANDLER 0x1c, 0; reserved
INTERRUPT_HANDLER 0x1d, 0; reserved
INTERRUPT_HANDLER 0x1e, 0; reserved
INTERRUPT_HANDLER 0x1f, 0; reserved

; 下面的数组记录了每个中断入口函数的指针
section .data
global handler_entry_table
handler_entry_table:
    dd interrupt_handler_0x00
    dd interrupt_handler_0x01
    dd interrupt_handler_0x02
    dd interrupt_handler_0x03
    dd interrupt_handler_0x04
    dd interrupt_handler_0x05
    dd interrupt_handler_0x06
    dd interrupt_handler_0x07
    dd interrupt_handler_0x08
    dd interrupt_handler_0x09
    dd interrupt_handler_0x0a
    dd interrupt_handler_0x0b
    dd interrupt_handler_0x0c
    dd interrupt_handler_0x0d
    dd interrupt_handler_0x0e
    dd interrupt_handler_0x0f
    dd interrupt_handler_0x10
    dd interrupt_handler_0x11
    dd interrupt_handler_0x12
    dd interrupt_handler_0x13
    dd interrupt_handler_0x14
    dd interrupt_handler_0x15
    dd interrupt_handler_0x16
    dd interrupt_handler_0x17
    dd interrupt_handler_0x18
    dd interrupt_handler_0x19
    dd interrupt_handler_0x1a
    dd interrupt_handler_0x1b
    dd interrupt_handler_0x1c
    dd interrupt_handler_0x1d
    dd interrupt_handler_0x1e
    dd interrupt_handler_0x1f

其中有一段代码如下

%macro INTERRUPT_HANDLER 2
interrupt_handler_%1:
%ifn %2
    push 0x20222202
%endif
    push %1; 压入中断向量号,跳转到中断入口
    jmp interrupt_entry
%endmacro

是宏函数,比如针对INTERRUPT_HANDLER 0x00, 0,宏函数展开后就是

interrupt_handler_0x00:
    push 0x20222202
    push 0x00
    jmp interrupt_entry

针对INTERRUPT_HANDLER 0x08, 1,展开为

interrupt_handler_0x08:
    push 0x08
    jmp interrupt_entry

为什么要有些push 0x20222202?

因为有些异常,系统会自动压入错误码,针对第二个参数为1的,就会自动压入,因此针对第二个参数为0的,我们手动压入,此时的0x20222202是我们自己设置的

最后我们在interrupt_init中初始化256个中断描述符表

// 一共128个大小的中断描述符表,然后从0开始,0x20个异常中断描述符
#define ENTRY_SIZE 0x20

gate_t idt[IDT_SIZE];
pointer_t idt_ptr; // 本身这个变量是针对全局描述符表,因为中断描述符表的指针一样,所以公用

// extern void interrupt_handler();
handler_t handler_table[IDT_SIZE];
extern handler_t handler_entry_table[ENTRY_SIZE];

static char *messages[] = {
    "#DE Divide Error\0",
    "#DB RESERVED\0",
    "--  NMI Interrupt\0",
    "#BP Breakpoint\0",
    "#OF Overflow\0",
    "#BR BOUND Range Exceeded\0",
    "#UD Invalid Opcode (Undefined Opcode)\0",
    "#NM Device Not Available (No Math Coprocessor)\0",
    "#DF Double Fault\0",
    "    Coprocessor Segment Overrun (reserved)\0",
    "#TS Invalid TSS\0",
    "#NP Segment Not Present\0",
    "#SS Stack-Segment Fault\0",
    "#GP General Protection\0",
    "#PF Page Fault\0",
    "--  (Intel reserved. Do not use.)\0",
    "#MF x87 FPU Floating-Point Error (Math Fault)\0",
    "#AC Alignment Check\0",
    "#MC Machine Check\0",
    "#XF SIMD Floating-Point Exception\0",
    "#VE Virtualization Exception\0",
    "#CP Control Protection Exception\0",
};

// 异常情况执行的函数
void exception_handler(int vector)
{
    char *message = NULL;
    if (vector < 22)
    {
        message = messages[vector];
    }
    else
    {
        message = messages[15];
    }

    printk("Exception : [0x%02X] %s \n", vector, messages[vector]);
    // 阻塞
    while (true)
        ;
}

void interrupt_init()
{
    // 初始化0x20个异常处理函数
    for (size_t i = 0; i < ENTRY_SIZE; i++)
    {
        gate_t *gate = &idt[i];
        // gate->offset0 = (u32)interrupt_handler & 0xffff;
        // gate->offset1 = ((u32)interrupt_handler >> 16) & 0xffff;
        handler_t handler = handler_entry_table[i];

        gate->offset0 = (u32)handler & 0xffff;
        gate->offset1 = ((u32)handler >> 16) & 0xffff;
        gate->selector = 1 << 3; // 代码段
        gate->reserved = 0;      // 保留不用
        gate->type = 0b1110;     // 中断门
        gate->segment = 0;       // 系统段
        gate->DPL = 0;           // 内核态
        gate->present = 1;       // 有效
    }

    for (size_t i = 0; i < ENTRY_SIZE; i++)
    {
        handler_table[i] = exception_handler;
    }

    idt_ptr.base = (u32)idt;
    idt_ptr.limit = sizeof(idt) - 1;

    asm volatile("lidt idt_ptr\n");
}

可能大家看代码不懂,我先说下具体的思路,现在有一个大小为256的中断描述符表,在这里我们初始化异常的相关函数,因此初始化0x20个,首先对于每一个,其对应的函数地址为handler_entry_table[i],也就是interrupt_handler_0x1b这些,而每个interrupt_handler_0x1b都是call [handler_table + eax * 4],其实handler_table是一个表,也是0x20个,因此每一个异常都去调用interrupt_handler_0xxx,然后interrupt_handler_0xxx调用handler_table,因此有了下面的代码

for (size_t i = 0; i < ENTRY_SIZE; i++)
{
    handler_table[i] = exception_handler;
}

只不过这里都是将每个异常的真实执行函数exception_handler都是一样的,都是打印一句话,我们来看下具体实现

// 异常情况执行的函数
void exception_handler(int vector)
{
    char *message = NULL;
    if (vector < 22)
    {
        message = messages[vector];
    }
    else
    {
        message = messages[15];
    }

    printk("Exception : [0x%02X] %s \n", vector, messages[vector]);
    // 阻塞
    while (true)
        ;
}

这样就基本实现了异常的处理机制

1

评论 (1)

取消
  1. 头像
    xyz
    Windows 10 · Google Chrome

    测试邮箱

    回复