回过头来发现,其实人生中发生的每一件事情就和面试一样,有优点,有缺点,如果是缺点的话我们快速的将这个bug补足,不就没有这个漏洞了~
也是人生发生每一件事情的意义~
简单来说,一件事情可以让你更完完善,也就是这件事情的意义!
前言
之前阅读过Linux0.11的源码,但是还是觉得远远还不够,随后入坑了这门mit6.s081. 享受lab带来的快乐~
环境配置
网页:https://pdos.csail.mit.edu/6.828/2021/tools.html
先说环境,笔者直接在VMWare上的Ubuntu22.04上实验的,废话少说,开干~
1 sudo apt-get install git build-essential gdb-multiarch qemu-system-misc gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu
直接克隆即可
1 2 3 4 5 6 7 git clone git://g.csail.mit.edu/xv6-labs-2021 penge@penge-virtual-machine:~/Desktop/OS/xv6-labs-2021$ ls conf gradelib.py LICENSE mkfs user grade-lab-util kernel Makefile README penge@penge-virtual-machine:~/Desktop/OS/xv6-labs-2021$ git checkout util Already on 'util' Your branch is up to date with 'origin/util' .
然后
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 penge@penge-virtual-machine ~/Desktop/OS/xv6-labs-2021 traps ± make clean rm -f *.tex *.dvi *.idx *.aux *.log *.ind *.ilg \*/*.o */*.d */*.asm */*.sym \ user/initcode user/initcode.out kernel/kernel fs.img \ mkfs/mkfs .gdbinit \ user/usys.S \ user/_cat user/_echo user/_forktest user/_grep user/_init user/_kill user/_ln user/_ls user/_mkdir user/_rm user/_sh user/_stressfs user/_usertests user/_grind user/_wc user/_zombie user/_alarmtest user/_call user/_bttest \ ph barrier penge@penge-virtual-machine ~/Desktop/OS/xv6-labs-2021 traps ± make riscv64-linux-gnu-gcc -c -o kernel/entry.o kernel/entry.S riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/kalloc.o kernel/kalloc.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/string.o kernel/string.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/main.o kernel/main.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/vm.o kernel/vm.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/proc.o kernel/proc.c riscv64-linux-gnu-gcc -c -o kernel/swtch.o kernel/swtch.S riscv64-linux-gnu-gcc -c -o kernel/trampoline.o kernel/trampoline.S riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/trap.o kernel/trap.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/syscall.o kernel/syscall.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/sysproc.o kernel/sysproc.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/bio.o kernel/bio.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/fs.o kernel/fs.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/log.o kernel/log.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/sleeplock.o kernel/sleeplock.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/file.o kernel/file.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/pipe.o kernel/pipe.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/exec.o kernel/exec.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/sysfile.o kernel/sysfile.c riscv64-linux-gnu-gcc -c -o kernel/kernelvec.o kernel/kernelvec.S riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/plic.o kernel/plic.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/virtio_disk.o kernel/virtio_disk.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/start.o kernel/start.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/console.o kernel/console.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/printf.o kernel/printf.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/uart.o kernel/uart.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -c -o kernel/spinlock.o kernel/spinlock.c riscv64-linux-gnu-gcc -Wall -Werror -O -fno-omit-frame-pointer -ggdb -DSOL_TRAPS -DLAB_TRAPS -MD -mcmodel=medany -ffreestanding -fno-common -nostdlib -mno-relax -I. -fno-stack-protector -fno-pie -no-pie -march=rv64g -nostdinc -I. -Ikernel -c user/initcode.S -o user/initcode.o riscv64-linux-gnu-ld -z max-page-size=4096 -N -e start -Ttext 0 -o user/initcode.out user/initcode.o riscv64-linux-gnu-objcopy -S -O binary user/initcode.out user/initcode riscv64-linux-gnu-objdump -S user/initcode.o > user/initcode.asm riscv64-linux-gnu-ld -z max-page-size=4096 -T kernel/kernel.ld -o kernel/kernel kernel/entry.o kernel/kalloc.o kernel/string.o kernel/main.o kernel/vm.o kernel/proc.o kernel/swtch.o kernel/trampoline.o kernel/trap.o kernel/syscall.o kernel/sysproc.o kernel/bio.o kernel/fs.o kernel/log.o kernel/sleeplock.o kernel/file.o kernel/pipe.o kernel/exec.o kernel/sysfile.o kernel/kernelvec.o kernel/plic.o kernel/virtio_disk.o kernel/start.o kernel/console.o kernel/printf.o kernel/uart.o kernel/spinlock.o riscv64-linux-gnu-objdump -S kernel/kernel > kernel/kernel.asm riscv64-linux-gnu-objdump -t kernel/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernel/kernel.sym
离谱的操作
ctrl + p
可以查看当前进程
ctrl-a x
可以退出sh(是按住ctrl还有a,松开手,再按下x,然后回车)
※ Note:退出 ctrl + a 放开后,再按下x
调试
这个方式有问题
1 2 3 gdb-multiarch kernel/kernel (gdb)set architecture riscv:rv64 (gdb)target remote localhost:25000
因此使用riscv-unknown-elf-gdb
这条命令~
1 penge@penge-virtual-machine:~/Desktop/OS/xv6-labs-2021$ sudo vim .gdbinit
将画红框的内容加入到.gdbinit
隐藏文件。
调试的话
一个终端开启调试
1 2 3 penge@penge-virtual-machine ~/Desktop/OS/xv6-labs-2021 traps ± sudo make qemu-gdb *** Now run 'gdb' in another window. qemu-system-riscv64 -machine virt -bios none -kernel kernel/kernel -m 128M -smp 1 -nographic -drive file=fs.img,if =none,format=raw,id =x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0 -S -gdb tcp::25000
一个终端远程连接即可
1 2 3 4 5 6 7 8 9 10 11 12 penge@penge-virtual-machine ~/Desktop/OS/xv6-labs-2021 traps ± riscv64-unknown-elf-gdb kernel/kernel GNU gdb (GDB) 13.1 (gdb) target remote localhost:25000 Remote debugging using localhost:25000 0x0000000000001000 in ?? () (gdb) b main Note: breakpoint 1 also set at pc 0x8000032e. Breakpoint 2 at 0x8000032e: file kernel/main.c, line 13. (gdb) c Continuing. Breakpoint 1, main () at kernel/main.c:1
然后就可用快乐的调试了~
Note:由于这个实验是多线程的
1 2 3 4 5 6 ifndef CPUSCPUS := 1 endif ifeq ($(LAB) ,fs)CPUS := 1 endif
为了调试方便,就先将这个设置成1了。
1 2 3 4 5 6 7 8 9 # b # c # n # p # si # display/i $pc # layout asm # display /3i $pc # p *path@6
闲话 :闲着看看项目行数
统计当前目录下的所有文件行数: wc -l *
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 penge@penge-virtual-machine ~/Desktop/OS/xv6-labs-2021 traps ± wc -l * 4 answers-traps.txt wc : conf: Is a directory 0 conf 3418 fs.img 70 grade-lab-traps 611 gradelib.py wc : kernel: Is a directory 0 kernel 24 LICENSE 410 Makefile wc : mkfs: Is a directory 0 mkfs wc : __pycache__: Is a directory 0 __pycache__ 47 README wc : user: Is a directory 0 user 62 xv6.out 4646 total
当前目录以及子目录的所有文件行数: find . * | xargs wc -l
Lab1
lab1就是简单的linux下编程,了解命令的含义编程即可。
Boot xv6 (easy )
这个就不截图了,能跑起了就好啦~
Implement the UNIX program sleep
for xv6; your sleep
should pause for a user-specified number of ticks. A tick is a notion of time defined by the xv6 kernel, namely the time between two interrupts from the timer chip. Your solution should be in the file user/sleep.c
.
简单来讲就是实现一个睡眠的程序,需要自己写在 user/sleep.c
里,一定要读一读实验手册里的hints,非常有用并且涉及到一些细节问题!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" int main (int argc, char *argv[]) { if (argc<=1 ){ printf ("args wrong!" ); exit (1 ); } int num = atoi (argv[1 ]); sleep (num); exit (0 ); }
细节
考虑输入为空或其他
将字符串转换为int类型
头文件的引用
pingpong (easy )
这个也不是很难
实验要求
Write a program that uses UNIX system calls to ‘‘ping-pong’’ a byte between two processes over a pair of pipes, one for each direction. The parent should send a byte to the child; the child should print “: received ping”, where is its process ID, write the byte on the pipe to the parent, and exit; the parent should read the byte from the child, print “: received pong”, and exit. Your solution should be in the file user/pingpong.c
.
大义就是:用两个管道接收父子进程的数据,父进程发送一个byte,然后子进程接收后打印,然后子进程发送父进程一个byte,最后父进程打印输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" #define READ 0 #define WRITE 1 int main (int argc, char *argv[]) { int p[2 ]; int q[2 ]; pipe (p),pipe (q); if (fork()==0 ){ char buff_child[7 ]; close (p[WRITE]); read (p[READ],buff_child,6 ); int child_pid = getpid (); printf ("%d: received ping\n" ,child_pid); close (p[READ]); close (q[READ]); write (q[WRITE], "father\n" , 7 ); close (q[WRITE]); }else { char buff_fa[6 ]; close (p[READ]); write (p[WRITE], "child\n" , 6 ); close (p[WRITE]); close (q[WRITE]); read (q[READ],buff_fa,7 ); int fa_pid = getpid (); printf ("%d: received pong\n" ,fa_pid); close (q[READ]); } exit (1 ); }
实验要求
Write a concurrent version of prime sieve using pipes. This idea is due to Doug McIlroy, inventor of Unix pipes. The picture halfway down this page and the surrounding text explain how to do it. Your solution should be in the file user/primes.c
.
用管道编写一个筛选质数的筛选器,可以参考超链接和下图:
目标是使用pipe和fork来设置管道。第一个进程将数字2到35输入到管道中。对于每个素数,您将安排创建一个进程,该进程通过一个管道从左边的邻居读取数据,并通过另一个管道向右边的邻居写入数据。由于 xv6的文件描述符和进程数量有限,因此第一个进程可以停留在35个。
思路
因为稍微有一点复杂,建议理一理思路后再动手。
每次将符合要求的全部传入管道中,第一个传的一定是素数,直接打印,然后将其他符合要求的再次传入管道并传给子进程。我自己的方法是递归处理,并没有完全利用到多进程的性能,如果有改进的办法,可以提出来讨论一下。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include "kernel/types.h" #include "kernel/stat.h" #include "user/user.h" #define READ 0 #define WRITE 1 #define N 35 #define NULL (int*)0 int ans[N];int main (int argc, char *argv[]) { int pipe_fa[2 ]; pipe (pipe_fa); int cnt=0 ; int main_pid=fork(); if (main_pid==0 ){ close (pipe_fa[WRITE]); while (1 ){ int pipe_child[2 ]; pipe (pipe_child); int num; int res_read = read (pipe_fa[READ],&num,sizeof (int )); if (res_read==0 ){ break ; } ans[cnt++]=num; printf ("prime %d\n" ,num); while (1 ){ int res_read_ = read (pipe_fa[READ],&num,sizeof (int )); if (res_read_==0 ){ break ; } if (num%ans[cnt-1 ]){ write (pipe_child[WRITE],&num,sizeof (int )); } } close (pipe_child[WRITE]); int pid = fork(); if (pid == 0 ){ pipe_fa[READ] = dup (pipe_child[READ]); close (pipe_child[READ]); }else { close (pipe_child[READ]); wait (NULL ); exit (0 ); } } exit (0 ); }else { close (pipe_fa[READ]); for (int i=2 ;i<=35 ;i++){ write (pipe_fa[WRITE],&i,sizeof (int )); } close (pipe_fa[WRITE]); wait (NULL ); } exit (0 ); }
Lab2
System call tracing (moderate )
In this assignment you will add a system call tracing feature that may help you when debugging later labs. You’ll create a new trace
system call that will control tracing. It should take one argument, an integer “mask”, whose bits specify which system calls to trace. For example, to trace the fork system call, a program calls trace(1 << SYS_fork)
, where SYS_fork
is a syscall number from kernel/syscall.h
. You have to modify the xv6 kernel to print out a line when each system call is about to return, if the system call’s number is set in the mask. The line should contain the process id, the name of the system call and the return value; you don’t need to print the system call arguments. The trace
system call should enable tracing for the process that calls it and any children that it subsequently forks, but should not affect other processes.
在这个作业中,您将添加一个系统调用跟踪特性,它可以帮助您调试以后的实验室。您将创建一个新的跟踪系统调用来控制跟踪。它应该采用一个参数,一个整数“掩码”,其位指定哪个系统调用跟踪。例如,为了跟踪 fork 系统调用,程序调用 trace (1 < < SYS _ fork) ,其中 SYS _ fork 是 kernel/syscall.h 中的系统调用号。如果在掩码中设置了系统调用的编号,则必须修改 xv6内核,以便在每个系统调用即将返回时输出一行。这一行应该包含进程 id、系统调用的名称和返回值; 您不需要打印系统调用参数。跟踪系统调用应该能够跟踪调用它的进程及其随后分支的任何子进程,但不应影响其他进程。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 $ trace 32 grep hello README 3: syscall read -> 1023 3: syscall read -> 966 3: syscall read -> 70 3: syscall read -> 0 $ $ trace 2147483647 grep hello README 4: syscall trace -> 0 4: syscall exec -> 3 4: syscall open -> 3 4: syscall read -> 1023 4: syscall read -> 966 4: syscall read -> 70 4: syscall read -> 0 4: syscall close -> 0 $ $ grep hello README $ $ trace 2 usertests forkforkfork usertests starting test forkforkfork: 407: syscall fork -> 408408: syscall fork -> 409 409: syscall fork -> 410 410: syscall fork -> 411 409: syscall fork -> 412 410: syscall fork -> 413 409: syscall fork -> 414 411: syscall fork -> 415 ...
先聊下添加系统调用的步骤:
添加系统调用主要有以下几步:
在user/user.h中添加系统调用函数的定义。
在user/usys.pl中添加入口,这个文件将会在make后生成user/usys.S文件,在该汇编文件中,每个函数就只有三行,将系统调用号通过li(load imm)存入a7寄存器,之后使用ecall进入内核态,最后返回。
1 2 3 4 5 6 7 8 # generated by usys.pl - do not edit #include "kernel/syscall.h" .global fork fork: li a7, SYS_fork ecall ret .global exit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #define KERNBASE 0x80000000L #define PHYSTOP (KERNBASE + 128*1024*1024) void kinit () { initlock (&kmem.lock, "kmem" ); freerange (end, (void *)PHYSTOP); } void freerange (void *pa_start, void *pa_end) { char *p; p = (char *)PGROUNDUP ((uint64)pa_start); for (; p + PGSIZE <= (char *)pa_end; p += PGSIZE) kfree (p); } void kfree (void *pa) { struct run *r; if (((uint64)pa % PGSIZE) != 0 || (char *)pa < end || (uint64)pa >= PHYSTOP) panic ("kfree" ); memset (pa, 1 , PGSIZE); r = (struct run*)pa; acquire (&kmem.lock); r->next = kmem.freelist; kmem.freelist = r; release (&kmem.lock); }
kfree
函数的主要功能是将一个内存页标记为空闲,并将其添加到空闲内存链表中。它首先检查传入的地址是否有效,然后填充内存页以捕获悬空引用,最后将内存页添加到空闲链表中,并确保操作是线程安全的。这样可以确保内存页在后续的内存分配中可以被重新使用。
整个详细流程可见: