Part C: Preemptive Multitasking and Inter-Process communication (IPC)
这最后一个部分需要修改内核来抢占那些不配合的环境,并且允许进程之间显式地传递消息。
时钟中断和抢占
user/spin
测试程序fork出一个子进程,子进程获得CPU控制之后执行一个死循环。父进程和kernel都不能重新获得CPU了。这从保护操作系统免受用户态环境的bug和恶意代码来看明显不是一个理想情况,因为任何的用户态环境都可以轻松地让系统停滞。为了让kernel可以抢占用户环境,强制接管CPU控制,我们必须扩展JOS kernel支持来自时钟硬件的外部硬件中断。
中断规则
外部中断称作IRQ。总共有16个IRQ,分别标记为从0到15号IRQ。从IRQ到IDT表项的映射不是固定的。picirq.c
中的pic_init
把0到15号映射到IRQ_OFFSET
到IRQ_OFFSET+15
在inv/trap.h
之中,IRQ_OFFSET
被设置为十进制数32,因此IDT表从从32到47对应IRQ0到15.这么选择是为了让硬件中断和处理器异常区分开不至于造成困惑。
在JOS中,相比于xv6 Unix,我们做了一个关键简化。在kernel中外部中断总是被关掉的。外部中断由eflags
寄存器中的FL_IF
位控制。当这个位被设置的时候,外部中断被打开。我们将会只通过保存和恢复eflags
寄存器的方式来修改FL_IF
位。
需要确保在用户环境运行的时候FL_IF
标志位是开启的,这样可以当中断来的时候进入kernel通过中断处理程序处理。否则,中断被屏蔽或忽略,直到中断被重新启用。我们在bootloader中最开始的指令中屏蔽了中断,到目前为止还没有重新开启过。
Exercise 13 要求修改kern/trap.c
以及kern/trapentry.S
来初始化IDT中恰当的表项。并且修改kern/env.c
中的env_alloc()
来确保用户环境运行的时候中断总是开启的。
并且把sched_halt()
中的sti
指令去掉注释,这样空闲的CPU就不会屏蔽中断了。
注意处理器在调用硬件中断处理程序的时候不会向内核栈压入错误码。
env.c
// Enable interrupts while in user mode.
// LAB 4: Your code here.
e->env_tf.tf_eflags |= FL_IF;
trapentry.S
TRAPHANDLER_NOEC(IRQ_0, IRQ_OFFSET + 0)
TRAPHANDLER_NOEC(IRQ_1, IRQ_OFFSET + 1)
TRAPHANDLER_NOEC(IRQ_2, IRQ_OFFSET + 2)
TRAPHANDLER_NOEC(IRQ_3, IRQ_OFFSET + 3)
TRAPHANDLER_NOEC(IRQ_4, IRQ_OFFSET + 4)
TRAPHANDLER_NOEC(IRQ_5, IRQ_OFFSET + 5)
TRAPHANDLER_NOEC(IRQ_6, IRQ_OFFSET + 6)
TRAPHANDLER_NOEC(IRQ_7, IRQ_OFFSET + 7)
TRAPHANDLER_NOEC(IRQ_8, IRQ_OFFSET + 8)
TRAPHANDLER_NOEC(IRQ_9, IRQ_OFFSET + 9)
TRAPHANDLER_NOEC(IRQ_10, IRQ_OFFSET + 10)
TRAPHANDLER_NOEC(IRQ_11, IRQ_OFFSET + 11)
TRAPHANDLER_NOEC(IRQ_12, IRQ_OFFSET + 12)
TRAPHANDLER_NOEC(IRQ_13, IRQ_OFFSET + 13)
TRAPHANDLER_NOEC(IRQ_14, IRQ_OFFSET + 14)
TRAPHANDLER_NOEC(IRQ_15, IRQ_OFFSET + 15)
trap.c
void IRQ_0();
void IRQ_1();
void IRQ_2();
void IRQ_3();
void IRQ_4();
void IRQ_5();
void IRQ_6();
void IRQ_7();
void IRQ_8();
void IRQ_9();
void IRQ_10();
void IRQ_11();
void IRQ_12();
void IRQ_13();
void IRQ_14();
void IRQ_15();
SETGATE(idt[IRQ_OFFSET + 0], 0, GD_KT, &IRQ_0, 0);
SETGATE(idt[IRQ_OFFSET + 1], 0, GD_KT, &IRQ_1, 0);
SETGATE(idt[IRQ_OFFSET + 2], 0, GD_KT, &IRQ_2, 0);
SETGATE(idt[IRQ_OFFSET + 3], 0, GD_KT, &IRQ_3, 0);
SETGATE(idt[IRQ_OFFSET + 4], 0, GD_KT, &IRQ_4, 0);
SETGATE(idt[IRQ_OFFSET + 5]