資源
題目:完善中斷初始化和處理
請完成編碼工作和回答如下問題:
-
中斷描述符表(也可簡稱為保護模式下的中斷向量表)中一個表項占多少字節?其中哪幾位代表中斷處理代碼的入口?
-
請編程完善kern/trap/trap.c中對中斷向量表進行初始化的函數idt_init。在idt_init函數中,依次對所有中斷入口進行初始化。使用mmu.h中的SETGATE宏,填充idt數組內容。每個中斷的入口由tools/vectors.c生成,使用trap.c中聲明的vectors數組即可。
-
請編程完善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表實際上是初始化每個中斷向量的這些屬性。
-
題目已經提供中斷向量的門類型和DPL的設置方法:除了系統調用的門類型為陷阱門、DPL=3外,其他中斷的門類型均為中斷門、DPL均為0.
-
中斷處理函數的段選擇子及偏移量的設置要參考kern/trap/vectors.S文件:由該文件可知,所有中斷向量的中斷處理函數地址均保存在__vectors數組中,該數組中第i個元素對應第i個中斷向量的中斷處理函數地址。而且由文件開頭可知,中斷處理函數屬於.text的內容。因此,中斷處理函數的段選擇子即.text的段選擇子GD_KTEXT。從kern/mm/pmm.c可知.text的段基址為0,因此中斷處理函數地址的偏移量等於其地址本身。
-
完成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中的代碼如下所示。答案有幾處地方比我寫得好:
- 我定義的sel變量是多余的,浪費內存,直接使用GD_KTEXT即可。
- 設置DPL時使用DPL_KERNEL, DPL_USER代替0和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;
}