ucore lab1 練習6—完善中斷初始化和處理


練習6:完善中斷初始化和處理 (需要編程)

請完成編碼工作和回答如下問題:

  1. 中斷描述符表(也可簡稱為保護模式下的中斷向量表)中一個表項占多少字節?其中哪幾位代表中斷處理代碼的入口?
  2. 請編程完善kern/trap/trap.c中對中斷向量表進行初始化的函數idt_init。在idt_init函數中,依次對所有中斷入口進行初始化。使用mmu.h中的SETGATE宏,填充idt數組內容。每個中斷的入口由tools/vectors.c生成,使用trap.c中聲明的vectors數組即可。
  3. 請編程完善trap.c中的中斷處理函數trap,在對時鍾中斷進行處理的部分填寫trap函數中處理時鍾中斷的部分,使操作系統每遇到100次時鍾中斷后,調用print_ticks子程序,向屏幕上打印一行文字”100 ticks”。

【注意】除了系統調用中斷(T_SYSCALL)使用陷阱門描述符且權限為用戶態權限以外,其它中斷均使用特權級(DPL)為0的中斷門描述符,權限為內核態權限;而ucore的應用程序處於特權級3,需要采用`int 0x80`指令操作(這種方式稱為軟中斷,軟件中斷,Tra中斷,在lab5會碰到)來發出系統調用請求,並要能實現從特權級3到特權級0的轉換,所以系統調用中斷(T_SYSCALL)所對應的中斷門描述符中的特權級(DPL)需要設置為3。

要求完成問題2和問題3 提出的相關函數實現,提交改進后的源代碼包(可以編譯執行),並在實驗報告中簡要說明實現過程,並寫出對問題1的回答。完成這問題2和3要求的部分代碼后,運行整個系統,可以看到大約每1秒會輸出一次”100 ticks”,而按下的鍵也會在屏幕上顯示。

提示:可閱讀小節“中斷與異常”。

中斷描述符表

操作系統是由中斷驅動的,用於當某事件發生時,可以主動通知cpu及os進行處理,主要的中斷類型有外部中斷、內部中斷(異常)、軟中斷(陷阱、系統調用)。

  • 外部中斷:用於cpu與外設進行通信,當外設需要輸入或輸出時主動向cpu發出中斷請求;
  • 內部中斷:cpu執行期間檢測到不正常或非法條件(如除零錯、地址訪問越界)時會引起內部中斷;
  • 系統調用:用於程序使用系統調用服務。

當中斷發生時,cpu會得到一個中斷向量號,作為IDT(中斷描述符表)的索引,IDT表起始地址由IDTR寄存器存儲,cpu會從IDT表中找到該中斷向量號相應的中斷服務程序入口地址,跳轉到中斷處理程序處執行,並保存當前現場;當中斷程序執行完畢,恢復現場,跳轉到原中斷點處繼續執行。

IDT的表項為中斷描述符,主要類型有中斷門、陷阱門、調用門,其中中斷門與陷阱門格式如下所示:

image-20200730160344378

image-20200730160406067

中斷門與陷阱門作為IDT的表項,每個表項占據8字節,其中段選擇子和偏移地址用來代表中斷處理程序入口地址,具體先通過選擇子查找GDT對應段描述符,得到該代碼段的基址,基址加上偏移地址為中斷處理程序入口地址。

初始化IDT

vectors.S文件為各中斷處理程序的入口,示例如下:

.text
.globl __alltraps
.globl vector0
vector0:
  pushl $0
  pushl $0
  jmp __alltraps
.globl vector1
vector1:
  pushl $0
  pushl $1
  jmp __alltraps
// 省略
# vector table
.data
.globl __vectors
__vectors:
  .long vector0
  .long vector1
  .long vector2

__vectors在數據段,是存儲了各中斷處理程序入口地址的數組,每一個中斷處理程序依次將錯誤碼、中斷向量號壓棧(一些由cpu自動壓入錯誤碼的只壓入中斷向量號),再調用trapentry.S中的 __alltraps過程進行處理。

根據中斷門、陷阱門描述符格式使用SETGATE宏函數對IDT進行初始化,在這里先全部設為中斷門,中斷處理程序均在內核態執行,因此代碼段為內核的代碼段,DPL為內核態的0。

/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */
void idt_init(void) {
    extern uintptr_t __vectors[];

    for (int i = 0; i < 256; i++) {
        SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
    }

    lidt(&idt_pd);
}

在進行中斷處理時,會保存現場,將返回地址、中斷號、相關寄存器等數據保存到trapframe結構中:

/* registers as pushed by pushal */
struct pushregs {
    uint32_t reg_edi;
    uint32_t reg_esi;
    uint32_t reg_ebp;
    uint32_t reg_oesp;            /* Useless */
    uint32_t reg_ebx;
    uint32_t reg_edx;
    uint32_t reg_ecx;
    uint32_t reg_eax;
};

struct trapframe {
    struct pushregs tf_regs;
    uint16_t tf_gs;
    uint16_t tf_padding0;
    uint16_t tf_fs;
    uint16_t tf_padding1;
    uint16_t tf_es;
    uint16_t tf_padding2;
    uint16_t tf_ds;
    uint16_t tf_padding3;
    uint32_t tf_trapno;
    /* below here defined by x86 hardware */
    uint32_t tf_err;
    uintptr_t tf_eip;
    uint16_t tf_cs;
    uint16_t tf_padding4;
    uint32_t tf_eflags;
    /* below here only when crossing rings, such as from user to kernel */
    uintptr_t tf_esp;
    uint16_t tf_ss;
    uint16_t tf_padding5;
} __attribute__((packed));

__alltraps為各中斷處理程序的前置代碼,用於繼續在棧中完成trapframe結構,依次壓入ds、es、fs、gs、通用寄存器,並將數據段切換為內核數據段(代碼段在IDT初始化過程中設置為內核代碼段),最后壓入trapframe結構體指針作為trap函數的參數,再調用trap函數完成具體的中斷處理,代碼如下:

__alltraps:
    # push registers to build a trap frame
    # therefore make the stack look like a struct trapframe
    pushl %ds
    pushl %es
    pushl %fs
    pushl %gs
    pushal

    # load GD_KDATA into %ds and %es to set up data segments for kernel
    movl $GD_KDATA, %eax
    movw %ax, %ds
    movw %ax, %es

    # push %esp to pass a pointer to the trapframe as an argument to trap()
    pushl %esp

    # call trap(tf), where tf=%esp
    call trap

處理時鍾中斷

trap_dispatch函數根據trapframe獲取中斷號去處理相應中斷,處理時鍾中斷的代碼如下:

void trap(struct trapframe *tf) {
    // dispatch based on what type of trap occurred
    trap_dispatch(tf);
}

/* trap_dispatch - dispatch based on what type of trap occurred */
static void trap_dispatch(struct trapframe *tf) {
    char c;

    switch (tf->tf_trapno) {
    case IRQ_OFFSET + IRQ_TIMER:
        ticks++;
        
        if (ticks % TICK_NUM == 0) {
            print_ticks();
        }

        break;
    }
}

中斷返回

trap函數執行完中斷處理程序后,恢復現場,重新彈出各寄存器值,iret指令彈出cs、eip、eflags,跳轉到之前中斷的地方繼續執行。

注:此篇並未考慮特權級變化時的中斷處理情況

執行結果

image-20200730204740976

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM