Part C: Preemptive Multitasking and Inter-Process communication (IPC)
在Lab4的最后一部分,我们将修改内核来实现抢占式调度和进程间通信的功能。
Clock Interrupts and Preemption
运行user/spin测试程序,该测试程序会创建一个陷入死循环的子进程,内核和父进程都不会重新获得cpu,就在用户进程中保护系统不产生BUG或受恶意代码影响而言,这显然不是一种理想的情况.因为任何用户进程通过让自身陷入死循环都能让整个系统停止,并且永不放弃CPU,为了让内核能够从一个运行进程中夺回cpu的控制权.我们必须扩展JOS内核来支持外部的时钟硬件中断。
Interrupt discipline
外部中断指IRQS,有16中可能的IRQS,编号从0到15,从IRQ编号到IDT表项的映射不固定.picirq.c中的pic_init将IRQS 0-15映射到IDT表项IRQ_OFFSET到IRQ_OFFSET+15。
inc/trap.h中,将IRQ_OFFSET定义为32,因此IDT中32-47的表项对应IRQS 0-15,例如时钟中断时IRQ 0,因此IDT[IRQ_OFFSET+0]包含了内核时钟中断处理程序的地址,选择此IRQ_OFFSET的目的是使设备中断不会与处理器异常重叠,避免引起混乱。 (实际上,在运行MS-DOS的PC的早期,IRQ_OFFSET实际上为零,这确实在处理硬件中断和处理处理器异常之间引起了很多混乱。
与XV6相比,我们在JOS中做了简化,处于内核态时,外部中断始终被禁止,外部中断由eflags寄存器的FL_IF标志位控制(inc/mmu.h),当该位被设置时,开外部中断,由于我们的简化.JOS中我们只能通过在进入或离开用户态时,对eflags寄存器进行修改来控制外部中断。
我们得确保用户态时,FL_IF标志被设置,这样才能检测到发生的外部中断并进行处理,否则,外部中断会被屏蔽或直到开中断前都被忽视,我们可以通过the bootloader中的第一条指令来屏蔽中断。
练习13:修改kern/trapentry.S和kern/trap.c来初始化IDT中的表项并且为IRQs0-15的外部中断提供处理函数,然后修改kern/env.c中的env_alloc()确保用户进程在开中断状态下运行,还要取消对sched_halt()中sti指令的注释.这样空闲的cpu也不会屏蔽中断,当触发硬件中断处理函数时,处理器并不会压入错误码,重新阅读 section 9.2 of the 80386 Reference Manual或者