- Compare kern/mpentry.S side by side with boot/boot.S. Bearing in mind thatkern/mpentry.S is compiled and linked to run above
KERNBASE
just like everything else in the kernel, what is the purpose of macroMPBOOTPHYS
? Why is it necessary inkern/mpentry.S but not inboot/boot.S? In other words, what could go wrong if it were omitted inkern/mpentry.S?
Hint: recall the differences between the link address and the load address that we have discussed in Lab 1.
问题一:
static void
boot_aps(void)
{
extern unsigned char mpentry_start[], mpentry_end[]; //分别为mpentry的起始地址和mpentry的终止地址
void *code;
struct Cpu *c;
// Write entry code to unused memory at MPENTRY_PADDR
code = KADDR(MPENTRY_PADDR);
memmove(code, mpentry_start, mpentry_end - mpentry_start); //将mpentry移动到MPENTRY_PADDR页面中
// Boot each AP one at a time
for (c = cpus; c < cpus + ncpu; c++) {
if (c == cpus + cpunum()) // We've started already.
continue;
// Tell mpentry.S what stack to use
mpentry_kstack = percpu_kstacks[c - cpus] + KSTKSIZE;
// Start the CPU at mpentry_start
lapic_startap(c->cpu_id, PADDR(code));
// Wait for the CPU to finish some basic setup in mp_main()
while(c->cpu_status != CPU_STARTED)
;
}
}
#define MPBOOTPHYS(s) ((s) - mpentry_start + MPENTRY_PADDR)
但是在AP的保护模式打开之前,是没有办法寻址到3G以上的空间的,因此用MPBOOTPHYS是用来计算相应的物理地址的。
但是在boot.S中,由于尚没有启用分页机制,所以我们能够指定程序开始执行的地方以及程序加载的地址;但是,在mpentry.S的时候,由于主CPU已经处于保护模式下了,因此是不能直接指定物理地址的,给定线性地址,映射到相应的物理地址是允许的。
Question
- It seems that using the big kernel lock guarantees that only one CPU can run the kernel code at a time. Why do we still need separate kernel stacks for each CPU? Describe a scenario in which using a shared kernel stack will go wrong, even with the protection of the big kernel lock.
问题二:
我们描述这样一种情况,考虑CPU0和CPU1
(1)CPU0运行中,遇到持续等待某个中断发生
(2)CPU1开始运行,CPU1将保存在task state中的寄存器的值恢复
(3)CPU1持续等待某个中断发生
(4)CPU0等待的中断发生,切换到CPU0
问题出现,因为CPU1保存在堆栈中的内容并没有弹出,因此此时转向CPU0时,共享栈的内容会出错。
Question
- In your implementation of
env_run()
you should havecalledlcr3()
. Before and after the call tolcr3()
, your code makes references (at least it should)to the variablee
, the argument toenv_run
.Upon loading the%cr3
register, the addressing contextused by the MMU is instantly changed. But a virtualaddress (namelye
) has meaning relative to a givenaddress context--the address context specifies the physical address towhich the virtual address maps. Why can the pointere
bedereferenced both before and after the addressing switch?
因为进程的切换是在内核中的,而内核的地址是固定的,也就是无论是哪个进程的页目录,内核都映射到相同的地址,因此,不会出现问题。