[OS][MIT 6.828] Lab 4: Preemptive Multitasking

Part A: Multiprocessor Support and Cooperative Multitasking

Multiprocessor Support

1.Application Processor Bootstrap

routine chain in BSP to active AP

  1. static struct mpconf *mpconfig(struct mp **pmp): Search for an MP configuration table, invoked at the beginning of mp_init().
  2. void mp_init(void): Initialize variables bootcpu, cpus ...
  3. void lapic_init(void): Initialize LAPIC of BP, i.e., write values out to registers of BP.
  4. static void boot_aps(void): Load mpentry_start into physical address 0x7000, invoke lapic_startap() to active AP.
  5. void lapic_startap(uint8_t apicid, uint32_t addr): Send startup IPI (twice!) to enter code.

routine chain in AP to set up processor

  1. mpentry_start: Similar to start in boot.S.
  2. void mp_main(void): Similar to bootmain(). Up to here, the AP has been activated and wait to hold the kernel lock.

2.Per-CPU State and Initialization

NULL

3.Locking

JOS uses the simplest big kernel lock, so no more than one environment can run in kernel mode.

I had a question of the code below in the beginning. From mpentry_start in mpentry.S, which calls mp_main, I learned that mp_main was already in kernel mode, so why lock_kernel was invoked in mp_main. I thought lock_kernel should be invoked at least before mp_main. Finally, I found I was stupid. I forgot the essence of lock. Only when access sharing data will you need a lock, and the code in mp_main before lock_kernel and in mpentry.S only sets up registers private to each CPU. Last but not least, code in user mode has no privilege to invoke lock_kernel & unlock_kernel, so how stupid was I _.

// Setup code for APs
void mp_main(void)
{
	// We are in high EIP now, safe to switch to kern_pgdir 
	lcr3(PADDR(kern_pgdir));
	cprintf("SMP: CPU %d starting
", cpunum());

	lapic_init();
	env_init_percpu();
	trap_init_percpu();
	xchg(&thiscpu->cpu_status, CPU_STARTED); // tell boot_aps() we're up

	// Now that we have finished some basic setup, call sched_yield()
	// to start running processes on this CPU.  But make sure that
	// only one CPU can enter the scheduler at a time!
	//
	// Your code here:
	lock_kernel();
	sched_yield();
}

Round-Robin Scheduling

NULL currently

System Calls for Environment Creation

NULL currently

Part B: Copy-on-Write Fork

User-level page fault handling

When user-level page fault occurs, first the CPU will trap into kernel mode following the interrupt mechanism. The Kernel will switch stack to user exception stack on behalf of user environment, and then restart the user environment running a designated user-level page fault handler. Then the user-level page fault handler will return directly to the faulting code on the original stack without transfer to kernel stack.

In a word, the stack switch like this: User Stack -> Kernel Stack -> User Exception Stack -> User Stack

steps to register user-level page fault handler the 1st time

  1. sys_page_alloc() :Allocate 1 physical page for va [UXSTACKTOP, UXSTACKTOP - PGSIZE), invoked by set_pgfault_handler().
  2. sys_env_set_pgfault_upcall() in lib/syscall.c: Wrapper of the function below. Use syscall() to enter kernel mode. Invoked by set_pgfault_handler().
  3. sys_env_set_pgfault_upcall() in kern/syscall.c: Set the page fault upcall by modifying struct Env's 'env_pgfault_upcall' field.
  4. set_pgfault_handler(): Set the page fault handler, i.e., set up the function pointer variable _pgfault_handler. Return here from step 2.

routine chain to handle user-level page fault

  1. page_fault_handler() :For routine chain before this routine, refer to 'lab3, summary of the function chain when handle a trap'. This routine save utf, set eip to env_pgfault_upcall, set esp within exception stack, and call env_run() to return to user mode.
  2. _pgfault_upcall(): Env.env_pgfault_upcall will be setted to point to this function.
  3. _pgfault_handler(): This function is registered by the user environment in the steps as described above.
原文地址:https://www.cnblogs.com/-zyq/p/14008377.html