# xv6 Kernel-3: Startup + Organization ### xv6 開機的三大步驟 1. `entry.S`: set up stack 1. `start.c`: machine mode 1. `main.c`: supervisor mode * modes of xv6 1. machine mode: 極少的 kernel 會使用 machine mode 1. supervisor mode: 多數 kernel 都是使用這個 mode 1. user mode: user program 使用的 mode ### `entry.S` 的解析 ```asm= # qemu -kernel loads the kernel at 0x80000000 # and causes each hart (i.e. CPU) to jump there. # kernel.ld causes the following code to # be placed at 0x80000000. .section .text .global _entry _entry: # set up a stack for C. # stack0 is declared in start.c, # with a 4096-byte stack per CPU. # sp = stack0 + (hartid * 4096) la sp, stack0 li a0, 1024*4 csrr a1, mhartid addi a1, a1, 1 mul a0, a0, a1 add sp, sp, a0 # jump to start() in start.c call start spin: j spin ``` * [csr](https://ithelp.ithome.com.tw/articles/10289643) ### `main.c` 的解析 ```clike= #include "types.h" #include "param.h" #include "memlayout.h" #include "riscv.h" #include "defs.h" volatile static int started = 0; // start() jumps here in supervisor mode on all CPUs. void main() { if(cpuid() == 0){ consoleinit(); printfinit(); printf("\n"); printf("xv6 kernel is booting\n"); printf("\n"); kinit(); // physical page allocator kvminit(); // create kernel page table kvminithart(); // turn on paging procinit(); // process table trapinit(); // trap vectors trapinithart(); // install kernel trap vector plicinit(); // set up interrupt controller plicinithart(); // ask PLIC for device interrupts binit(); // buffer cache iinit(); // inode table fileinit(); // file table virtio_disk_init(); // emulated hard disk userinit(); // first user process __sync_synchronize(); started = 1; } else { while(started == 0) ; __sync_synchronize(); printf("hart %d starting\n", cpuid()); kvminithart(); // turn on paging trapinithart(); // install kernel trap vector plicinithart(); // ask PLIC for device interrupts } scheduler(); } ``` 當 xv6 開機時,只有其中一個 CPU 在做初始化了動作,其他的 CPU 就要等到這個 CPU 把事情做完之後,才會開始做各自的事情。 * 在 `main()` 的最後,會執行 `scheduler()` (可能跟 yunikoen 的 scheduler 的角色有點像) ### `kernel/proc.c: scheduler()` * 這是在 `main()` 最後一行執行的 function ```clike= void scheduler(void) { struct proc *p; struct cpu *c = mycpu(); c->proc = 0; for(;;){ // Avoid deadlock by ensuring that devices can interrupt. intr_on(); for(p = proc; p < &proc[NPROC]; p++) { acquire(&p->lock); if(p->state == RUNNABLE) { // Switch to chosen process. It is the process's job // to release its lock and then reacquire it // before jumping back to us. p->state = RUNNING; c->proc = p; swtch(&c->context, &p->context); // Process is done running for now. // It should have changed its p->state before coming back. c->proc = 0; } release(&p->lock); } } } ``` * `scheduler()` 是只有一個 thread 在執行嗎? ### 註記 * `__sync_syncronize()` 的作用: * 必須要先前的程式都執行完了,才可以往下執行 * `kernel/param.h` 裏面有紀錄了一些參數 ## 參考資料 * [xv6 Kernel-3: Startup + Organization](https://www.youtube.com/watch?v=6hhJ6JN95As&list=PLbtzT1TYeoMhTPzyTZboW_j7TPAnjv9XB&index=4)