回过头来发现,其实人生中发生的每一件事情就和面试一样,有优点,有缺点,如果是缺点的话我们快速的将这个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这条命令~

image-20240805213407210

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 CPUS
CPUS := 1
endif
ifeq ($(LAB),fs)
CPUS := 1
endif

为了调试方便,就先将这个设置成1了。

1
2
3
4
5
6
7
8
9
# b //设置断点(breakpoint)
# c //继续运行(Continue)--到断点处就会停止
# n //单步调试
# p //打印变量内容
# si //断点定位
# display/i $pc //每次停止时,都可以显示下一条指令的反汇编
# layout asm //可以显示当前的汇编指令
# display /3i $pc //如果您希望在单步执行程序时自动显示下3条指令,可以使用display命令
# p *path@6 //这里是打印path指向的内容,打印长度为6(如果不写@6那么只能打印一个char)

闲话:闲着看看项目行数

统计当前目录下的所有文件行数: 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)

这个就不截图了,能跑起了就好啦~

sleep (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);
// child
if(fork()==0){
char buff_child[7];
// === child first stage ===
close(p[WRITE]);
read(p[READ],buff_child,6);
int child_pid = getpid();
printf("%d: received ping\n",child_pid);
close(p[READ]);
// === second stage ===
close(q[READ]);
write(q[WRITE], "father\n", 7);
close(q[WRITE]);
}else{
char buff_fa[6];
// ===fa first stage ===
close(p[READ]);
write(p[WRITE], "child\n", 6);
close(p[WRITE]);
// === second stage ===
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);
}

primes (moderate)/(hard)

实验要求

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个。

image-20240805214744151

思路

因为稍微有一点复杂,建议理一理思路后再动手。

每次将符合要求的全部传入管道中,第一个传的一定是素数,直接打印,然后将其他符合要求的再次传入管道并传给子进程。我自己的方法是递归处理,并没有完全利用到多进程的性能,如果有改进的办法,可以提出来讨论一下。

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);
// printf("debug:answer generate%d\n",num);
while(1){
int res_read_ = read(pipe_fa[READ],&num,sizeof(int));
// printf("debug:read num:%d res_read_:%d\n",num,res_read_);
if(res_read_==0){
break;
}
if(num%ans[cnt-1]){
write(pipe_child[WRITE],&num,sizeof(int));
}
}
close(pipe_child[WRITE]);
// printf("debug:finish %d\n",num);
// 循环迭代
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]);
// printf("debug:main input[2~35]\n");
wait(NULL); // 等待子进程结束
}
// 输出结果
// for(int i=0;i<cnt;i++){
// printf("%d\n",ans[i]);
// }
exit(0);
}

image-20240805214107322

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 -> 408
408: 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
  • 在kernel/syscall.h中定义系统调用号。

  • 在kernel/syscall.c的syscalls函数指针数组中添加对应的函数。在syscall函数中,先读取trapframe->a7获取系统调用号,之后根据该系统调用号查找syscalls数组中的对应的处理函数并调用。

image-20240805235021773

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);
}

// Free the page of physical memory pointed at by v,
// which normally should have been returned by a
// call to kalloc(). (The exception is when
// initializing the allocator; see kinit above.)
void
kfree(void *pa)
{
struct run *r;

if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");

// Fill with junk to catch dangling refs.
memset(pa, 1, PGSIZE);

r = (struct run*)pa;

acquire(&kmem.lock);
r->next = kmem.freelist;
kmem.freelist = r;
release(&kmem.lock);
}

kfree 函数的主要功能是将一个内存页标记为空闲,并将其添加到空闲内存链表中。它首先检查传入的地址是否有效,然后填充内存页以捕获悬空引用,最后将内存页添加到空闲链表中,并确保操作是线程安全的。这样可以确保内存页在后续的内存分配中可以被重新使用。

整个详细流程可见:

image-20240805234706950