你好,我是 Yama,歡迎來到我的網站。
Contact Me
- Email: [email protected]
- GitHub: @9501sam
課程影片連結: 6.S081 Fall 2020 Lecture 5: RISC-V Calling Convention and Stack Frames 這篇文章目的在於整理這個影片的重點,主要有以下的重點 如何用 GDB 追蹤 user program RISC-V 的 Calling Convention RISC-V 的 register (Caller and Callee saved) RISC-V 的 Stack Frame 影片中的範例程式並沒有附在 lab 中,要自己新增: 新增影片中的範例程式 kernel/defs.h // demos.c void demo1(void); void demo2(void); void demo3(void); void demo4(void); void demo5(void); void demo6(void); void demo7(void); // asmdemo.S int sum_to(int); int sum_then_double(int); kernel/demos.c #include "types.h" #include "riscv.h" #include "defs.h" #include "date.h" #include "param.h" #include "memlayout.h" #include "spinlock.h" #include "proc.h" #include "sysinfo.h" void demo1() { printf("Result: %d\n", sum_to(5)); } void demo2() { printf("Tesult: %d\n", sum_ten_double(5)); } void _demo3(char a, char b, char c, char d, char e, char f, char g, char h, char i, char j) { printf("%d, %d, %d, %d, %d, %d, %d, %d, %d, %d\n", a, b, c, d, e, f, g, h, i, j); } void demo3() { _demo3('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'); } int dummymain(int argc, char *argv[]) { int i = 0; for (; i < argc; i++) { printf("Argument %d: %s\n", i, argv[i]); } return 0; } void demo4() { char *args[] = {"foo", "bar", "baz"}; int result = dummymain(sizeof(args)/sizeof(args[0]), args); if (result < 0) panic("Demo 4"); } int _dummy(int n) { int div = 5; int sum = 0; for (int i=0; i < n; i++) { if (i % div == 0) sum += i; } return sum; } void demo5() { printf("Demo\n"); int top = 50; int result = _dummy(top); printf("Result: %d\n", result); } void demo6() { int sum = 0; for (int i = 1; i < 10; i++) { sum += 3 *i; printf("%d\n", sum_to(i)); } printf("Sum: %d\n", sum); } struct Person { int id; int age; // char *name; }; void printPerson(struct Person *p) { printf("Person %d (%d)\n", p->id, p->age); // printf("Name:%s: %d (%d)\n", p->name, p->id, p->age); } void demo7() { // Structs struct Person p; p.id = 1215; p.age = 22; // p.name = "Nick"; printPerson(&p); } kernel/asmdemo.S .section .text .global sum_to /* int sum_to() { int acc = 0; for (int i = 0; i <= n; i++) { acc += i; } return acc; } */ sum_to: mv t0, a0 # t0 <- a0 li a0, 0 # a0 <- 0 loop: add a0, a0, t0 # a0 <- a0 + t0 addi t0, t0, -1 # t0 <- t0 - 1 bnez t0, loop # if t0 != 0: pc <- loop ret .global sum_then_double sum_then_double: addi sp, sp, -16 sd ra, 0(sp) call sum_to li t0, 2 mul a0, a0, t0 ld ra, 0(sp) addi sp, sp, 16 ret Makefile OBJS = \ $K/entry.o \ $K/kalloc.o \ $K/string.o \ ... $K/plic.o \ $K/virtio_disk.o \ $K/demos.o \ $K/asmdemo.o 新增 system call demo() user/usys.pl 加上 entry(demo); ...
本文目標:照著以下步驟就可以看到整個 system call 的過程 整個過程大致上都是照著這個影片做的,但其中有幾個步驟稍稍的不同。 2. 用 gdb-multiarch debug xv6 的方式 這裡會需要開啟 2 個終端機, 先在其中一個終端機輸入 make qemu-gdb # 這是被 debug 的對象 在另一個終端機輸入 gdb-multiarch # 開啟 debuger 會開始針對上面的那個終端機中的程式進行除錯 gdb-multiarch 是透過 .gdbinit 這個檔案找到 debug 的對象的 # .gdbinit set confirm off set architecture riscv:rv64 target remote 127.0.0.1:26000 # 透過這個來找到 qemu symbol-file kernel/kernel set disassemble-next-line auto set riscv use-compressed-breakpoints yes 動機:$ 的出現 make qemu 後 xv6 開機時,會顯示的畫面 ...
Lab 連結: lab traps 課程影片連結: 6.S081 Fall 2020 Lecture 5: RISC-V Calling Convention and Stack Frames 6.S081 Fall 2020 Lecture 6: Isolation & System Call Entry/Exit RISC-V assembly (easy) It will be important to understand a bit of RISC-V assembly, which you were exposed to in 6.1910 (6.004). There is a file user/call.c in your xv6 repo. make fs.img compiles it and also produces a readable assembly version of the program in user/call.asm. Read the code in call.asm for the functions g, f, and main. The instruction manual for RISC-V is on the reference page. Here are some questions that you should answer (store the answers in a file answers-traps.txt): ...
課程影片連結: 6.S081 Fall 2020 Lecture 8: Page Faults Page Fault 回顧 virtual memory 的特性 isolation level of indirection 在原先 xv6 的設計中 pagetable 的設計是 static 現在在 page fault 的時候做一些動作,可以設計為 dynamic 的 mapping Information neede 造成 page fault 的 virtual memory stval register The type of page fault scause: R/W/Instruction Virtual address of instruction that cause page fault 這是為了在處理完 page fault 之後,可以回到原本的地方繼續執行 trampframe->epc Allocation: sbrk() 原先的方式:eager allocatoin 可變更為:Lazy Allocation 1. Lazy Allocation 先把 sys_sbrk() 變更為: ...
Lab 連結: Lab: xv6 lazy page allocation Eliminate allocation from sbrk() (easy) Your first task is to delete page allocation from the sbrk(n) system call implementation, which is the function sys_sbrk() in sysproc.c. The sbrk(n) system call grows the process’s memory size by n bytes, and then returns the start of the newly allocated region (i.e., the old size). Your new sbrk(n) should just increment the process’s size (myproc()->sz) by n and return the old size. It should not allocate memory – so you should delete the call to growproc() (but you still need to increase the process’s size!). ...
課程影片連結: 6.S081 Fall 2020 Lecture 9: Interrupts Terms PLIC UART Interrupts 的意義 跟 syscall, traps 很像 Interrupts 從哪裡來? 一些 CPU 的 fig programming device, memory mapped I/O UART store/load to registers Case Study: $ ls 的出現 kerenl/main.c kernel/console.c: consoleinit() kernel/uart.c: uartinit() kernel/plic.c: plicinit() kernel/plic.c: plicinithart() kernel/proc.c: scheduler() 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(); } UART kernel/console.c: consoleinit() void consoleinit(void) { initlock(&cons.lock, "cons"); uartinit(); // <-- // connect read and write system calls // to consoleread and consolewrite. devsw[CONSOLE].read = consoleread; devsw[CONSOLE].write = consolewrite; } kernel/uart.c: uartinit() void uartinit(void) { // disable interrupts. WriteReg(IER, 0x00); // special mode to set baud rate. WriteReg(LCR, LCR_BAUD_LATCH); // LSB for baud rate of 38.4K. WriteReg(0, 0x03); // MSB for baud rate of 38.4K. WriteReg(1, 0x00); // leave set-baud mode, // and set word length to 8 bits, no parity. WriteReg(LCR, LCR_EIGHT_BITS); // reset and enable FIFOs. WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR); // enable transmit and receive interrupts. WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); initlock(&uart_tx_lock, "uart"); } 在這裡可以設定一些 UART 的參數,像是 baud rate 等等 ...
Lab 連結:Lab: Copy-on-Write Fork for xv6 題目解析 The problem The fork() system call in xv6 copies all of the parent process’s user-space memory into the child. If the parent is large, copying can take a long time. Worse, the work is often largely wasted: fork() is commonly followed by exec() in the child, which discards the copied memory, usually without using most of it. On the other hand, if both parent and child use a copied page, and one or both writes it, the copy is truly needed. ...
課程影片連結: 6.S081 Fall 2020 Lecture 11: Thread Switching 追蹤 Context Switch 的過程 先看一下影片中的範例程式 user/spin.c: #include "kernel/types.h" #include "user/user.h" int main(int argc, char **argv) { int pid; char c; pid = fork(); if (pid == 0) { c = '/'; } else { printf("parent pid is %d, child is %d", getpid(), pid); c = '\\'; } for (int i = 0; ; i++) { if (i % 1000000 == 0) write(2, &c, 1); } exit(0); } 在做的事情基本上是 ...
Lab 連結:Lab: Multithreading Uthread: switching between threads (moderate) In this exercise you will design the context switch mechanism for a user-level threading system, and then implement it. To get you started, your xv6 has two files user/uthread.c and user/uthread_switch.S, and a rule in the Makefile to build a uthread program. uthread.c contains most of a user-level threading package, and code for three simple test threads. The threading package is missing some of the code to create a thread and to switch between threads. ...
課程影片連結: 6.S081 Fall 2020 Lecture 10: Multiprocessors and Locks Deadlock acquire(&l) acquire(&l) release(&l) Locks vs. Performance 越多 lock 雖然可能可以比較安全,但也會出現效能上的不佳 Case study UART kernel/uart.c struct spinlock uart_tx_lock; void uartputc(int c) { acquire(&uart_tx_lock); if(panicked){ for(;;) ; } while(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){ // buffer is full. // wait for uartstart() to open up space in the buffer. sleep(&uart_tx_r, &uart_tx_lock); } uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c; uart_tx_w += 1; uartstart(); release(&uart_tx_lock); } void uartintr(void) { // read and process incoming characters. while(1){ int c = uartgetc(); if(c == -1) break; consoleintr(c); } // send buffered characters. acquire(&uart_tx_lock); uartstart(); release(&uart_tx_lock); } Lock rules protect data structure Lock 的實做 broken lock void acquire(struct spinlock *lk) // does not work! { for(;;) { if(lk->locked == 0) { lk->locked = 1; break; } } } 這是一個錯誤的實做方法,因為可能有兩個 CPU 進入到 if (l->locked == 0) ...