中断向量表一个表项占用8字节,其中2-3字节是段选择子,0-1字节和6-7字节拼成位移,入口地址=段选择子+段内偏移量。
(2) 完善kern/trap/trap.c中对中断向量表进行初始化的函数idt_init
可以在/lab1/kern/mm/mmu.h中可以找到SETGATE函数,查找其具体操作。 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); //设置IDT }
lidt(&idt_pd); //载入IDT表
(3) 完善trap.c中的中断处理函数trap,在对时钟中断进行处理的部分填写trap函数 case IRQ_OFFSET + IRQ_TIMER:
ticks ++; //一次中断累加1 if (ticks % TICK_NUM == 0) { print_ticks(); }
break;
扩展:
(1) 内核态切换到用户态:
lab1_switch_to_user(void) {
asm volatile (
\
// esp-8 为下一步复制的栈帧留好 tf_ss和tf_esp的位置 \ \ : : \ ); } case T_SWITCH_TOU: if (tf->tf_cs != USER_CS) { switchk2u = *tf;
switchk2u.tf_cs = USER_CS;
switchk2u.tf_ds = switchk2u.tf_es = switchk2u.tf_ss = USER_DS; switchk2u.tf_esp = (uint32_t)tf + sizeof(struct trapframe) - 8;
//在执行int120前系统在核心态,int不会引起栈的切换
switchk2u.tf_eflags |= FL_IOPL_MASK;
*((uint32_t *)tf - 1) = (uint32_t)&switchk2u; }
break;
最后iret时返回5个值。 (2) 用户态切换到内核态:
lab1_switch_to_kernel(void) { asm volatile ( \
\ :
: \ ); }
case T_SWITCH_TOK: if (tf->tf_cs != KERNEL_CS) { tf->tf_cs = KERNEL_CS; tf->tf_ds = tf->tf_es = KERNEL_DS; tf->tf_eflags &= ~FL_IOPL_MASK; //定位临时栈的栈顶
switchu2k = (struct trapframe *)(tf->tf_esp - (sizeof(struct trapframe) - 8)); //复制
memmove(switchu2k, tf, sizeof(struct trapframe) - 8); //在执行int120前系统在核心态,int会引起栈的切换,iret不会引起 //栈的切换
*((uint32_t *)tf - 1) = (uint32_t)switchu2k; /*设置临时栈,指向switchu2k,这样iret返回时,CPU会从switchu2k恢复数据, 而不是从现有栈恢复数据。*/
} break;
最后iret返回三个值
Question:
将用户态转到内核态不用临时栈,直接在原来的栈进行操作?