《ucore lab1 exercise6》實驗報告


資源

  1. ucore在線實驗指導書
  2. 我的ucore實驗代碼

題目:完善中斷初始化和處理

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

  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”。

完成這問題2和3要求的部分代碼后,運行整個系統,可以看到大約每1秒會輸出一次”100 ticks”,而按下的鍵也會在屏幕上顯示。

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

解答

問題1的回答

問題1:中斷描述符表(也可簡稱為保護模式下的中斷向量表)中一個表項占多少字節?其中哪幾位代表中斷處理代碼的入口?
答:中斷描述符表一個表項占8個字節,其結構如下:

  • bit 63..48: offset 31..16
  • bit 47..32: 屬性信息,包括DPL、P flag等
  • bit 31..16: Segment selector
  • bit 15..0: offset 15..0

其中第1632位是段選擇子,用於索引全局描述符表GDT來獲取中斷處理代碼對應的段地址,再加上第015、48~63位構成的偏移地址,即可得到中斷處理代碼的入口。

完善idt_init函數

idt_init函數的功能是初始化IDT表。IDT表中每個元素均為門描述符,記錄一個中斷向量的屬性,包括中斷向量對應的中斷處理函數的段選擇子/偏移量、門類型(是中斷門還是陷阱門)、DPL等。因此,初始化IDT表實際上是初始化每個中斷向量的這些屬性。

  1. 題目已經提供中斷向量的門類型和DPL的設置方法:除了系統調用的門類型為陷阱門、DPL=3外,其他中斷的門類型均為中斷門、DPL均為0.

  2. 中斷處理函數的段選擇子及偏移量的設置要參考kern/trap/vectors.S文件:由該文件可知,所有中斷向量的中斷處理函數地址均保存在__vectors數組中,該數組中第i個元素對應第i個中斷向量的中斷處理函數地址。而且由文件開頭可知,中斷處理函數屬於.text的內容。因此,中斷處理函數的段選擇子即.text的段選擇子GD_KTEXT。從kern/mm/pmm.c可知.text的段基址為0,因此中斷處理函數地址的偏移量等於其地址本身。

  3. 完成IDT表的初始化后,還要使用lidt命令將IDT表的起始地址加載到IDTR寄存器中。

根據以上分析,及注釋中的提示,不難完成編碼,如下所示。

extern uintptr_t __vectors[];

/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */
void idt_init(void) {
    uint32_t pos;
    uint32_t sel = GD_KTEXT;

    /* along: how to set istrap and dpl? */
    for (pos = 0; pos < 256; pos++) {
        SETGATE(idt[pos], 0, sel, __vectors[pos], 0);
    }
        
    SETGATE(idt[128], 1, sel, __vectors[128], 3);

    lidt(&idt_pd);
}

lab1_result中的代碼如下所示。答案有幾處地方比我寫得好:

  1. 我定義的sel變量是多余的,浪費內存,直接使用GD_KTEXT即可。
  2. 設置DPL時使用DPL_KERNEL, DPL_USER代替0和3,可讀性更好。
  3. 將extern __vectors放在函數內部,使其僅在本函數內可見,結構上更合理。(是嗎?)

不過這里有個問題:答案設置系統調用的門描述符時,中斷向量為什么是T_SWITCH_TOK(121)而不是T_SYSCALL(128)?

/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */
void idt_init(void) {
    extern uintptr_t __vectors[];
    int i;
    for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {
        SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
    }
	// set for switch from user to kernel
    SETGATE(idt[T_SWITCH_TOK], 0, GD_KTEXT, __vectors[T_SWITCH_TOK], DPL_USER);
	// load the IDT
    lidt(&idt_pd);
}

完善trap函數

trap函數只是直接調用了trap_dispatch函數,而trap_dispatch函數實現對各種中斷的處理,題目要求我們完成對時鍾中斷的處理,實現非常簡單:定義一個全局變量ticks,每次時鍾中斷將ticks加1,加到100后打印"100 ticks",然后將ticks清零重新計數。代碼實現如下:

    case IRQ_OFFSET + IRQ_TIMER:
        if (((++ticks) % TICK_NUM) == 0) {
            print_ticks();
            ticks = 0;
        }


免責聲明!

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



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