MIT 6.S081学习笔记(第二章)
〇、前言
本文主要完成MIT 6.S081 实验二:system call
一、Using gdb (easy)
Question requirements
In many cases, print statements will be sufficient to debug your kernel, but sometimes being able to single step through some assembly code or inspecting the variables on the stack is helpful.
To learn more about how to run GDB and the common issues that can arise when using GDB, check out this page.
To help you become familiar with gdb, run make qemu-gdb and then fire up gdb in another window (see the gdb bullet on the guidance page). Once you have two windows open, type in the gdb window:
我们启动 gdb:
$ make qemu-gdb
make qemu-gdb
命令通常是用于启动QEMU虚拟机并与GDB(GNU Debugger)连接以进行调试的命令。以下是它的主要作用:
- 启动QEMU虚拟机:make qemu-gdb 命令启动了一个QEMU虚拟机实例。QEMU是一个开源的虚拟化工具,它可以模拟多种处理器体系结构,并在模拟环境中运行操作系统和应用程序。
- 启用GDB调试:通过运行:make qemu-gdb,将启用GDB调试模式,使得QEMU虚拟机处于可调试状态。
- 建立GDB连接:一旦QEMU虚拟机启动,它将等待GDB连接。可以使用GDB来连接到虚拟机,以便执行调试操作,如断点设置、变量查看、单步执行等。
- 调试目标程序:通常,
make qemu-gdb
命令会与要调试的目标程序相关联。
这时候,在另一个相同目录下的终端中,输入:
$ gdb-multiarch -x .gdbinit
gdb-multiarch -x .gdbinit
这个命令的作用是使用多体系结构(multi-architecture)版本的GDB,同时执行一个包含GDB命令的脚本文件 .gdbinit。各个部分的含义如下:
-
gdb-multiarch:这是GDB的一个命令,表示启动多体系结构版本的GDB。多体系结构版本的GDB支持在不同的CPU架构上进行调试。
-
-x .gdbinit:这是一个选项,指示GDB在启动时执行指定的脚本文件 .gdbinit。脚本文件通常包含GDB命令,这些命令在GDB启动时自动执行,用于配置调试环境、设置断点、查看变量等。
我的.gdbinit
文件内容如下:
set confirm off
set architecture riscv:rv64
target remote 127.0.0.1:25000
symbol-file kernel/kernel
set disassemble-next-line auto
set riscv use-compressed-breakpoints yes
这一系列命令是用于配置GDB(GNU Debugger)以进行RISC-V 64位架构(rv64)的目标程序调试的设置。以下是这些命令的含义:
- set confirm off:这个命令将GDB的确认提示设置为关闭。通常,当你执行一些可能影响程序执行的命令时,GDB会要求你确认。通过将确认关闭,可以在不需要确认的情况下执行这些命令。
- set architecture riscv:rv64:这个命令设置目标程序的体系结构为RISC-V 64位架构(rv64)。这告诉GDB,将要调试的程序是针对RISC-V 64位架构编译的。
- target remote 127.0.0.1:25000:这个命令告诉GDB要连接到本地IP地址 127.0.0.1 和端口号 25000 上的远程调试目标。这通常用于与调试代理或硬件调试器建立连接。这个端口可能不是总是 25000,设置要视情况而定(一般都不需要设定,自动生成)
- symbol-file kernel/kernel:这个命令指定了符号文件的路径,该符号文件与要调试的程序相关联。符号文件包含了程序的符号信息,如函数名称、变量名称等,可以帮助GDB更好地理解和调试程序。
- set disassemble-next-line auto:这个命令设置GDB在逐行反汇编时自动显示反汇编代码。这使得在单步执行时,GDB会显示每一行的反汇编指令,以便更容易理解程序的执行过程。
- set riscv use-compressed-breakpoints yes:这个命令启用了对RISC-V压缩指令集(Compressed Instructions)的断点支持。压缩指令集是RISC-V的一种特性,允许使用更紧凑的指令编码。启用这个选项后,GDB会使用压缩指令集来设置断点,以便与压缩指令集编译的程序一起工作。
在 gdb 窗口:
(gdb) b syscall
Breakpoint 1 at 0x8000203c: file kernel/syscall.c, line 133.
(gdb) c
Continuing.Thread 1 hit Breakpoint 1, syscall ()at kernel/syscall.c:133
133 {
(gdb) layout src
(gdb) backtrace
上面的步骤产生的结果:
#0 syscall () at kernel/syscall.c:133
#1 0x0000000080001d70 in usertrap ()at kernel/trap.c:67
#2 0x0505050505050505 in ?? ()
接着单步运行到这一行代码:
(gdb) n
(gdb) n
(gdb) p/x *p
$2 = {lock = {locked = 0x0, name = 0x800081b8,cpu = 0x0}, state = 0x4, chan = 0x0, killed = 0x0,xstate = 0x0, pid = 0x1, parent = 0x0,kstack = 0x3fffffd000, sz = 0x1000,pagetable = 0x87f73000, trapframe = 0x87f74000,context = {ra = 0x800014c2, sp = 0x3fffffde80,s0 = 0x3fffffdeb0, s1 = 0x80008d70, s2 = 0x80008940,s3 = 0x1, s4 = 0x0, s5 = 0x3, s6 = 0x80019a10,s7 = 0x8, s8 = 0x80019b38, s9 = 0x4, s10 = 0x1,s11 = 0x0}, ofile = {0x0 <repeats 16 times>},cwd = 0x80016e80, name = {0x69, 0x6e, 0x69, 0x74,0x63, 0x6f, 0x64, 0x65, 0x0, 0x0, 0x0, 0x0, 0x0,0x0, 0x0, 0x0}}
现在检查这个值:p->trapframe->a7
What is the value of p->trapframe->a7 and what does that value represent? (Hint: look user/initcode.S, the first user program xv6 starts.)
(gdb) p p->trapframe->a7
$3 = 7
可以看到,a7 的值为 7。
研究一下 kernel/proc.h中的trapframe 结构体中的字段:
struct trapframe {/* 0 */ uint64 kernel_satp; // kernel page table/* 8 */ uint64 kernel_sp; // top of process's kernel stack/* 16 */ uint64 kernel_trap; // usertrap()/* 24 */ uint64 epc; // saved user program counter/* 32 */ uint64 kernel_hartid; // saved kernel tp/* 40 */ uint64 ra;/* 48 */ uint64 sp;/* 56 */ uint64 gp;/* 64 */ uint64 tp;/* 72 */ uint64 t0;/* 80 */ uint64 t1;/* 88 */ uint64 t2;/* 96 */ uint64 s0;/* 104 */ uint64 s1;/* 112 */ uint64 a0;/* 120 */ uint64 a1;/* 128 */ uint64 a2;/* 136 */ uint64 a3;/* 144 */ uint64 a4;/* 152 */ uint64 a5;/* 160 */ uint64 a6;/* 168 */ uint64 a7; // a7普普通通/* 176 */ uint64 s2;/* 184 */ uint64 s3;/* 192 */ uint64 s4;/* 200 */ uint64 s5;/* 208 */ uint64 s6;/* 216 */ uint64 s7;/* 224 */ uint64 s8;/* 232 */ uint64 s9;/* 240 */ uint64 s10;/* 248 */ uint64 s11;/* 256 */ uint64 t3;/* 264 */ uint64 t4;/* 272 */ uint64 t5;/* 280 */ uint64 t6;
};
再来看下 syscall.h:
// System call numbers
#define SYS_fork 1
#define SYS_exit 2
#define SYS_wait 3
#define SYS_pipe 4
#define SYS_read 5
#define SYS_kill 6
#define SYS_exec 7
#define SYS_fstat 8
#define SYS_chdir 9
#define SYS_dup 10
#define SYS_getpid 11
#define SYS_sbrk 12
#define SYS_sleep 13
#define SYS_uptime 14
#define SYS_open 15
#define SYS_write 16
#define SYS_mknod 17
#define SYS_unlink 18
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
恍然大悟,原来 7 指的是 SYS_exec
,这意味着它加下来运行系统调用了:
void
syscall(void)
{int num;struct proc *p = myproc();num = p->trapframe->a7;// num = *(int*) 0;if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {// Use num to lookup the system call function for num, call it,// and store its return value in p->trapframe->a0p->trapframe->a0 = syscalls[num]();} else {printf("%d %s: unknown sys call %d\n",p->pid, p->name, num);p->trapframe->a0 = -1;}
}
What was the previous mode that the CPU was in?
查一下它运行前 CPU 的模式是什么:
(gdb) p/t $sstatus
$4 = 100010
**根据RISC-V特权指令,SPP位指示在进入监控模式之前执行的HART的权限级别。**创建陷阱时,如果陷阱源自用户模式,则 SPP 设置为 0,否则设置为 1。当执行 SRET 指令(请参阅第 3.3.2 节)以从陷阱处理程序返回时,如果 SPP 位为 0,则权限级别设置为用户模式,如果 SPP 位为 1,则设置为主管模式;然后将 SPP 设置为 0。
SPP 位的值为 0,表示进入内核进行系统调用之前的权限级别是用户模式。
根据RISC-V特权指令,SPP位指示在进入监控器模式之前执行的HART的权限级别。创建陷阱时,如果陷阱源自用户模式,则 SPP 设置为 0,否则设置为 1。当执行 SRET 指令从陷阱处理程序返回时,如果 SPP 位为 0,则权限级别设置为用户模式,如果 SPP 位为 1,则设置为主管模式;然后将 SPP 设置为 0。
如我们所见,SPP 位的值(从右向左数,第8位)为 0,表示进入内核进行系统调用之前的权限级别是用户模式。也就是说,该陷入是由用户造成的。
然后,将 kernel/syscall.c
中的语句num = p->trapframe->a7
换成 num = * (int *) 0
执行以下命令:
$ make qemu
Write down the assembly instruction the kernel is panicing at. Which
register corresponds to the variable num?
直接出错:
xv6 kernel is bootinghart 2 starting
hart 1 starting
scause 0x000000000000000d
sepc=0x0000000080002050 stval=0x0000000000000000
panic: kerneltrap
我们在汇编代码中直接搜:80002050
num = *(int*) 0;80002050: 00002683 lw a3,0(zero) # 0 <_entry-0x80000000>
可以看到,正是num = * (int *) 0
语句导致了错误。它给了 num 一个错误的值。
Why does the kernel crash? Hint: look at figure 3-3 in the text; is address 0 mapped in the kernel address space? Is that confirmed by the value in scause above?
重新启动,在发生错误的位置设置断点:
Thread 1 received signal SIGINT, Interrupt.
panic (s=s@entry=0x800083c0 "kerneltrap")at kernel/printf.c:127
(gdb) p $scause
$1 = 13
根据 RISC-V privileged instructions,13 代表着页面加载错误。
重新启动,直接打印出内核 panic时地用户进程名字:
(gdb) b *0x0000000080002050
Breakpoint 1 at 0x80002050: file kernel/syscall.c, line 138.
(gdb) c
Continuing.
[Switching to Thread 1.3]Thread 3 hit Breakpoint 1, syscall ()at kernel/syscall.c:138
138 num = *(int*) 0;
(gdb) p p->name
$1 = "initcode\000\000\000\000\000\000\000"
(gdb)
可以看出这个用户进程的名字是initcode
,这是 xv6 中第一个运行的进程。
我们还可以看看这个进程的 proc structure 长什么样:
(gdb) p *p
$2 = {lock = {locked = 0, name = 0x800081b8 "proc", cpu = 0x0}, state = RUNNING, chan = 0x0, killed = 0, xstate = 0, pid = 1, parent = 0x0, kstack = 274877894656, sz = 4096, pagetable = 0x87f73000, trapframe = 0x87f74000, context = {ra = 2147488962, sp = 274877898368, s0 = 274877898416, s1 = 2147519856, s2 = 2147518784, s3 = 1, s4 = 0, s5 = 3, s6 = 2147588624, s7 = 8, s8 = 2147588920, s9 = 4, s10 = 1, s11 = 0}, ofile = {0x0 <repeats 16 times>}, cwd = 0x80016e80 <itable+24>, name = "initcode\000\000\000\000\000\000\000"}
从这个proc结构
的信息中可以得出:
-
进程状态:state字段的值为RUNNING,表示这个进程的状态为运行中,即它当前正在执行。
-
进程标识:pid字段的值为1,表示这是一个进程的唯一标识符。通常情况下,进程的第一个进程(通常是init进程)的pid为1。
-
父进程:parent字段的值为0x0,表示这个进程没有父进程。这在操作系统中的进程树中通常是初始进程(init)的情况。
-
进程内存信息:sz字段的值为4096,表示这个进程的内存大小为4096字节。
-
页表信息:pagetable字段的值为0x87f73000,表示这个进程的页表的地址。
-
陷阱帧信息:trapframe字段的值为0x87f74000,表示这个进程的陷阱帧(trap frame)的地址。陷阱帧通常包含了在进程执行时发生陷阱或异常时的寄存器状态等信息。
-
上下文信息:context字段包含了一系列寄存器的值,包括返回地址(ra)、栈指针(sp)、一般寄存器(s0到s11)等。这些寄存器状态可能用于在进程切换或陷阱处理中。
-
当前工作目录:cwd字段的值为0x80016e80,表示当前工作目录的地址。这是进程在执行时的当前工作目录。
-
进程名:name字段的值为 “initcode”,表示这个进程的名字是 “initcode”。
二、System call tracing (moderate)
Question requirements
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.
Some hints
- Add $U/_trace to UPROGS in Makefile
- Run make qemu and you will see that the compiler cannot compile user/trace.c, because the user-space stubs for the system call don’t exist yet: add a prototype for the system call to user/user.h, a stub to user/usys.pl, and a syscall number to kernel/syscall.h. The Makefile invokes the perl script user/usys.pl, which produces user/usys.S, the actual system call stubs, which use the RISC-V ecall instruction to transition to the kernel. Once you fix the compilation issues, run trace 32 grep hello README; it will fail because you haven’t implemented the system call in the kernel yet.
- Add a sys_trace() function in kernel/sysproc.c that implements the new system call by remembering its argument in a new variable in the proc structure (see kernel/proc.h). The functions to retrieve system call arguments from user space are in kernel/syscall.c, and you can see examples of their use in kernel/sysproc.c.
- Modify fork() (see kernel/proc.c) to copy the trace mask from the parent to the child process.
- Modify the syscall() function in kernel/syscall.c to print the trace output. You will need to add an array of syscall names to index into.
- If a test case passes when you run it inside qemu directly but you get a timeout when running the tests using make grade, try testing your implementation on Athena. Some of tests in this lab can be a bit too computationally intensive for your local machine (especially if you use WSL).
只需要沿着步骤就可以完成这个实验:
1. Add $U/_trace\
to UPROGS
in Makefile
UPROGS=\...$U/_zombie\$U/_trace\
2. Add a prototype for the system call to user/user.h
, a stub to user/usys.pl
, and a syscall number to kernel/syscall.h
.
// user/user.h
// system calls
...
int uptime(void);
int trace(int);
// user/usys.pl
...
entry("uptime");
entry("trace");
// kernel/syscall.h
// System call numbers
...
#define SYS_close 21
#define SYS_trace 22
3. Add a sys_trace()
function in kernel/sysproc.c
that implements the new system call by remembering its argument in a new variable in the proc structure
(see kernel/proc.h
). The functions to retrieve system call arguments from user space are in kernel/syscall.c
, and you can see examples of their use in kernel/sysproc.c
.
// kernel/proc.h
// Per-process state
struct proc {...char name[16]; // Process name (debugging)uint64 syscall_trace;
};
// kernel/sysproc.c
uint64
sys_trace(void)
{int mask;argint(0, &mask);myproc()->syscall_trace = mask;return 0;
}
4. Modify fork in kernel/proc.c
to copy the trace mask from the parent to the child process.
int
fork(void)
{...safestrcpy(np->name, p->name, sizeof(p->name));np->syscall_trace = p->syscall_trace;pid = np->pid;...
}
5. Modify the syscall
function in kernel/syscall.c
to print the trace output.
// Prototypes for the functions that handle system calls.
...
extern uint64 sys_close(void);
extern uint64 sys_trace(void);// An array mapping syscall numbers from syscall.h
// to the function that handles the system call.
static uint64 (*syscalls[])(void) = {
...
[SYS_close] sys_close,
[SYS_trace] sys_trace,
};// 添加识别名
char* syscalls_name[23] = {"", "fork", "exit", "wait", "pipe", "read", "kill", "exec","fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime","open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace"};void
syscall(void)
{...num = p->trapframe->a7;if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {// Use num to lookup the system call function for num, call it,// and store its return value in p->trapframe->a0p->trapframe->a0 = syscalls[num]();if (p->syscall_trace & (1 << num)) {// print trace infoprintf("%d: syscall %s -> %d\n",p->pid, syscalls_name[num], p->trapframe->a0);}
}
做到这里,就可以测试了:
root@*******:~/xv6-labs-2023# ./grade-lab-syscall trace
make: 'kernel/kernel' is up to date.
== Test trace 32 grep == trace 32 grep: OK (3.2s)
== Test trace all grep == trace all grep: OK (0.8s)
== Test trace nothing == trace nothing: OK (1.0s)
== Test trace children == trace children: OK (27.2s)
root@iZhp37m9qv6vg0r4zidw3oZ:~/xv6-labs-2023#
可以看到全部通过了测试。
但是依然需要回答一些问题:
Q1: usys.pl
是什么文件?
usys.pl
是一个Perl脚本文件,用于生成名为 usys.S 的汇编语言源代码文件。这个脚本的目的是生成系统调用的存根(stubs),以便操作系统内核能够处理用户空间程序的系统调用请求。
具体来说,这个Perl脚本执行以下操作:
定义一个名为 entry 的Perl子例程,该子例程接受系统调用的名称作为参数,并生成与该系统调用相关的汇编代码。
使用 print 命令输出汇编代码,包括 .global 声明和汇编指令,以将系统调用名称与系统调用号码进行映射,并触发 ecall 指令以执行系统调用。
为一系列系统调用(如 fork、exit、read 等等)调用 entry 子例程,以生成相应的汇编代码。
输出注释,指出该文件是由 usys.pl 自动生成的,不应手动编辑。
生成的 usys.S 文件通常将作为操作系统内核的一部分,用于处理用户空间程序发起的系统调用请求。这种自动生成的方式可以确保系统调用存根的正确性,并简化了内核的开发和维护过程。
再来看看这个usys.S
:
.global trace
trace:li a7, SYS_traceecallret
li a7, SYS_trace
:这行代码将立即数 SYS_trace 加载到寄存器 a7 中。a7 寄存器通常用于存储系统调用号码,而 SYS_trace 是一个代表系统调用的符号常量,通常在C代码中定义,用于标识系统调用的类型。
ecall:这是一个特权指令,用于触发系统调用。当执行 ecall 指令时,处理器将进入特权模式(通常是监管模式),并跳转到操作系统内核中的相应系统调用处理程序。系统调用号码 a7 指示了要执行的系统调用类型。
ret:这是一个返回指令,用于返回到调用者的代码。在系统调用完成后,处理器会执行 ret 指令,将控制权返回给调用 SYS_trace 系统调用的代码。
Q2: 从用户态到内核态运行一个系统调用的过程大致是怎么样的?
用户态调用:在用户态运行的程序需要执行一个系统调用时,它会调用一个包含系统调用号码和参数的库函数,例如C库中的syscall函数。
void syscall(void) {int num;struct proc *p = myproc();num = p->trapframe->a7;if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {// 用 num 来查阅指向系统调用的函数入口// p->trapframe->a0 来保存系统调用的返回值p->trapframe->a0 = syscalls[num]();if(p->syscall_trace & (1 << num)){// 将 1 左移 num 位,然后与p->syscall_trace 进行与运算,判断是否满足某个条件printf("%d: syscall %s -> %d\n",p->pid, syscalls_name[num], p->trapframe->a0);}} else {printf("%d %s: unknown sys call %d\n",p->pid, p->name, num);p->trapframe->a0 = -1;}
}
系统调用号码:库函数会将系统调用号码和参数加载到寄存器中,通常使用a7寄存器(或者其他体系结构中的类似寄存器),其中a7用于存储系统调用号码。
触发系统调用:用户态程序执行特殊的指令,例如ecall(在RISC-V架构中)或int 0x80(在x86架构中)。这些指令触发了从用户态到内核态的切换,将控制权传递给操作系统内核。
切换到内核态:当发生系统调用指令时,处理器将当前的用户态上下文保存到特定的寄存器或内存区域中,包括程序计数器(PC)和用户态栈指针(SP)。然后,处理器切换到内核态,切换到操作系统内核的地址空间。
系统调用处理:操作系统内核根据系统调用号码确定要执行的系统调用,并使用传递的参数执行相应的操作。这可能涉及到访问硬件资源、文件系统、网络等等。内核执行完成后,将结果存储在适当的寄存器或内存位置。
返回用户态:内核执行完系统调用后,将保存的用户态上下文恢复回处理器,包括将用户态栈指针(SP)和程序计数器(PC)还原到之前保存的值。然后,处理器切换回用户态,并继续执行用户程序,返回到调用系统调用的下一条指令。
xv6 的系统调用过程大致如下所示:
三、Sysinfo (moderate)
Question requirements
In this assignment you will add a system call, sysinfo, that collects information about the running system. The system call takes one argument: a pointer to a struct sysinfo (see kernel/sysinfo.h). The kernel should fill out the fields of this struct: the freemem field should be set to the number of bytes of free memory, and the nproc field should be set to the number of processes whose state is not UNUSED. We provide a test program sysinfotest; you pass this assignment if it prints “sysinfotest: OK”.
Some hints
- Add $U/_sysinfotest to UPROGS in Makefile
- Run make qemu; user/sysinfotest.c will fail to compile. Add the system call sysinfo, following the same steps as in the previous assignment. To declare the prototype for sysinfo() in user/user.h you need predeclare the existence of struct sysinfo:
struct sysinfo; int sysinfo(struct sysinfo *)
;- Once you fix the compilation issues, run sysinfotest; it will fail because you haven’t implemented the system call in the kernel yet.
sysinfo needs to copy a struct sysinfo back to user space; see sys_fstat() (kernel/sysfile.c) and filestat() (kernel/file.c) for examples of how to do that using copyout().- To collect the amount of free memory, add a function to kernel/kalloc.c
- To collect the number of processes, add a function to kernel/proc.c
1. Add $U/_sysinfotest\
to UPROGS
in Makefile
.
UPROGS=\...$U/_trace\$U/_sysinfotest\
2. Add a prototype for the system call to user/user.h
, a stub to user/usys.pl
, and a syscall number to kernel/syscall.h
.
// user/user.h
// system calls
...
int trace(int);
struct sysinfo;
int sysinfo(struct sysinfo *);
// user/usys.pl
...
entry("trace");
entry("sysinfo");
// kernel/syscall.h
// System call numbers
...
#define SYS_trace 22
#define SYS_sysinfo 23
3. Add a sys_sysinfo
function in kernel/sysproc.c
that copies a struct sysinfo
back to user space.
// kernel/sysproc.c
...
#include "proc.h"
#include "sysinfo.h"
...
uint64
sys_info(void)
{uint64 addr;argaddr(0, &addr);struct sysinfo sinfo;sinfo.freemem = freemem_size();sinfo.nproc = count_proc();if (copyout(myproc()->pagetable, addr, (char *)&sinfo, sizeof(sinfo)) < 0)return -1;return 0;
}
4. Update the array of syscall names to index into in kernel/syscall.c
.
// Prototypes for the functions that handle system calls.
...
extern uint64 sys_trace(void);
extern uint64 sys_info(void);// An array mapping syscall numbers from syscall.h
// to the function that handles the system call.
static uint64 (*syscalls[])(void) = {
...
[SYS_trace] sys_trace,
[SYS_sysinfo] sys_info,
};const char *syscall_names[] = {
...
[SYS_trace] "trace",
[SYS_sysinfo] "sysinfo",
};
5. Add a function to kernel/kalloc.c
to collect the amount of free memory.
uint64
freemem_size(void)
{acquire(&kmem.lock); // prevent race conditionuint64 size = 0;struct run *r = kmem.freelist;while (r) {size++;r = r->next;}release(&kmem.lock);return size * PGSIZE;
}
6. Add a function to kernel/proc.c
to collect the number of processes.
uint64
count_proc(void)
{uint64 cnt = 0;for (int i = 0; i < NPROC; ++i) {if (proc[i].state != UNUSED) {cnt++;}}return cnt;
}
把系统调用的 helper 函数放进去:
// kalloc.c
...
void kinit(void);
uint64 freemem_size(void);
...
// proc.c
...
uint64 count_proc(void);
四、测试
root@********:~/xv6-labs-2023# ./grade-lab-syscall
make: 'kernel/kernel' is up to date.
== Test answers-syscall.txt ==
answers-syscall.txt: OK
== Test trace 32 grep == trace 32 grep: OK (1.8s)
== Test trace all grep == trace all grep: OK (1.0s)
== Test trace nothing == trace nothing: OK (1.0s)
== Test trace children == trace children: OK (25.3s)
== Test sysinfotest == sysinfotest: OK (3.9s)
== Test time ==
time: OK
Score: 40/40
root@iZhp37m9qv6vg0r4zidw3oZ:~/xv6-labs-2023#
相关文章:
MIT 6.S081学习笔记(第二章)
〇、前言 本文主要完成MIT 6.S081 实验二:system call 一、Using gdb (easy) Question requirements In many cases, print statements will be sufficient to debug your kernel, but sometimes being able to single step through some assembly code or inspe…...
L958. 二叉树的完全性检验 java
从1开始当下标,最后节点下标节点总数?true:false; /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { …...
阿里云对象存储OSS SDK的使用
官方文档 https://help.aliyun.com/zh/oss/developer-reference/java 准备工作 windows安装好JDK,这里使用JDK1.8为例 windows安装好IDEA,这里使用IDEA2022 登录阿里云控制台,通过免费试用OSS或开通OSS 步骤 配置访问凭证 有临时和长期…...
二、互联网技术——网络协议
文章目录 一、OSI与TCP/IP参考模型二、TCP/IP参考模型各层功能三、TCP/IP参考模型与对应协议四、常用协议与功能五、常用协议端口 一、OSI与TCP/IP参考模型 二、TCP/IP参考模型各层功能 三、TCP/IP参考模型与对应协议 例题:TCP/IP模型包含四个层次,由上至…...
初赛错题集
MPEG属于视频文件格式. UNIX,Mac OS属于操作系统. 中国计算机协会成立于()年。 A. 1961 B. 1962 C. 1971 D. 1972 Ans:B 五个本质不同的点在没有重边或者自环的情况下,组成不同的无向图的个数是: A. 10 B. 1024 C. 15 D. 120 Ans:B 解析&…...
Java Thread类详解
🙈作者简介:练习时长两年半的Java up主 🙉个人主页:程序员老茶 🙊 ps:点赞👍是免费的,却可以让写博客的作者开兴好久好久😎 📚系列专栏:Java全栈,…...
3_使用传统CNN网络训练图像分类模型
使用传统CNN网络训练图像分类模型 1. MNIST 首先,定义一下超参数等 import torch# dataset input_shape = 28 num_classes = 10# hyper batch_size = 64 num_epochs = 5 learning_rate = 1e-3# gpu device = torch.device(cuda...
Java 创建线程的方法
🙈作者简介:练习时长两年半的Java up主 🙉个人主页:程序员老茶 🙊 ps:点赞👍是免费的,却可以让写博客的作者开兴好久好久😎 📚系列专栏:Java全栈,…...
基于安卓android微信小程序的旅游app系统
项目介绍 随着人民生活水平的提高,旅游业已经越来越大众化,而旅游业的核心是信息,不论是对旅游管理部门、对旅游企业,或是对旅游者而言,有效的获取旅游信息,都显得特别重要.自助定制游将使旅游相关信息管理工作规范化、信息化、程序化,提供旅游景点、旅游线路,旅游新闻等服务本…...
C++设计模式-单件(Singleton)
目录 C设计模式-单件(Singleton) 一、意图 二、适用性 三、结构 四、参与者 五、代码 C设计模式-单件(Singleton) 一、意图 保证一个类仅有一个实例,并提供一个访问它的全局访问点。 二、适用性 当类只能有一…...
想做好接口测试,先把这些概念搞清楚了
接口一般来说有两种,一种是程序内部的接口,一种是系统对外的接口。 系统对外的接口 比如你要从别的网站或服务器上获取资源或信息,别人肯定不会把数据库共享给你,他只能给你提供一个他们写好的方法来获取数据,你引用…...
通过融合UGV的地图信息和IMU的惯性测量数据,实现对车辆精确位置和运动状态的估计和跟踪研究(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
『Linux』Linux环境搭建 | 阿里云云服务器白嫖 | Xshell环境配置
🔥博客主页: 小羊失眠啦 🔖系列专栏: C语言、Linux 🌥️每日语录:时间,都是公平的,不公平的,只是现在的自己,对未来的自己。 ❤️感谢大家点赞👍收…...
C++ 类和对象篇(五) 析构函数
目录 一、概念 1. 析构函数是什么? 2. 为什么要有析构函数? 3. 怎么用析构函数? 3.1 创建析构函数 3.2 调用析构函数 二、特性 三、由编译器生成的默认析构函数 四、对象的析构顺序 1. 局部对象 2. new出来的堆对象 3. 全局对象 一、概念 1…...
find 与 cp 命令组合使用
查找到文件后,拷贝到指定路径 find ~/Downloads/ -name *.torrent -exec cp {} ~/Downloads/myTorrent \;\;前面有个空格,要注意,这是固定结构,请不要尝试改变 上面命令是在Downloads 目标中查找后缀为torrent所有文件࿰…...
用VLD调查VC内存泄漏
一、发现内存泄漏 使用VS2022,发现提示有内存泄漏,检查了所有的new,确认都有相应的delete释放。 Detected memory leaks! Dumping objects -> {1914} normal block at 0x0000021FDFFBD2E0, 48 bytes long.Data: < >…...
【Java 进阶篇】使用 JDBCTemplate 执行 DQL 语句详解
在前面的文章中,我们已经学习了如何使用 Spring 的 JDBCTemplate 执行 DML(Data Manipulation Language)操作,包括插入、更新和删除操作。现在,让我们来深入了解如何使用 JDBCTemplate 执行 DQL(Data Query…...
了解了spring mvc web容器中一个http请求的全过程,能给我们提升多少武力值
继上一篇文章什么,这年头还有人不知道404_cow__sky的博客-CSDN博客后,有些同学发现,学了之后有啥用,有什么实际场景可以用到吗?程序员就是这样,不习惯于纸上谈兵,给一个场景show me code才是最实…...
【BBC新闻文章分类】使用 TF 2.0和 LSTM 的文本分类
一、说明 NLP上的许多创新是如何将上下文添加到词向量中。常见的方法之一是使用递归神经网络...
set和map的封装
目录 介绍 红黑树代码 set insert的迭代器转换问题 为什么会有这样的问题? 如何解决 代码 map 注意点 代码 介绍 set和map的底层都是红黑树,所以我们可以在自己实现的红黑树(简易版)的基础上,进行封装,成为简易的set和map 红黑树代码 #pragma once#include <…...
java基础练习--基础语法
预备知识:Java基本语法、分支、循环、数组和字符串 7-1 累加器 请你实现一个累加器。输入n个非负整数,输出他们的和。 1<n<1000,而每个数则<10000。 输入格式: 输入包括两行。 第一行:包括一个整数n,表示总共有n个数。 第二行:包…...
Android12 OTA编译差分包报错问题
前言 在Ubuntu 20.04.4 LTS系统中编译Android12 OTA差分包的时候提示如下报错log: Warning: releasetools script should be invoked as hermetic Python executable -- build and run ota_from_target_files directly. Traceback (most recent call last):File "./bu…...
现代c++手撸2309神经网络最简化版230901
用c++输入数据:vector<vector<float>> inputs = { {1, 1}, {1, 0} };数据targets={0,1}; 测试:vector<vector<float>> inputs22 = { {1, 0}, {1,1} }; 构建神经网络,例如:NeuralNetwork nn({ 2, 4, 1 }); 则网络有四层、输入层2个节点、输出层1个节…...
Qt之显示PDF文件
之前使用过mupdf库,能够成功显示pdf,但是我用着有BUG,不太理解它的代码,搞了好久都不行。后面又试了其他库,如pdfium、popler、下载了很多例程,都跑不起来!后面偶然得知xpdf库,看起来…...
[极客大挑战 2019]FinalSQL - 异或盲注
1、这题的关键是找注入点,如果选择用户名、密码作为输入点就麻烦了 2、注入点:按钮,点击就传id;当id1时,提示Click others 可以利用id的特性,构造异或匹配 payload: f"1^(ord(substr((select…...
【Go语言实战】(25) 分布式算法 MapReduce
MapReduce 写在前面 身为大数据专业的学生,其实大学我也多多少少接触过mapreduce,但是当时觉得这玩意太老了,觉得这和php一样会被时代淘汰。只能说当时确实太年轻了,没有好好珍惜那时候的学习资源… 现在回过头来看mapreduce&a…...
【网络安全-信息收集】网络安全之信息收集和信息收集工具讲解(提供工具)
工具下载百度网盘链接(包含所有用到的工具): 百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固,支持教育网加速,支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.…...
战火使命ssr排名,战火使命角色强度排行
在战火使命中,很多玩家都在关注SSR角色的强度排行,那么,下面就为大家分享一下小编整理的最新战火使命ssr排名,一起来看看吧。 关注【娱乐天梯】,获取内部福利号 一、SSR角色排名榜: 1. 克拉拉、艾蕾娜、杰西…...
CSS之linear-gradient( ) 函数—背景颜色渐变设计
目录 linear-gradient( ) 函数 简介: 语法: 详解: 例如: linear-gradient( ) 函数 简介: linear-gradient 函数是 CSS 中用于创建线性渐变的函数。它接受一个或多个参数,并使用这些参数创建一个渐变。…...
[Unity]未能加载一个或多个断点问题
【背景】 大家2023国庆快乐,虽然是假期,我还是继续码些文章。 今天写项目时遇到个环境问题,新建脚本时双击调起VS编辑器,忽然提示无法加载一个或多个断点(当时忘记截图了,现在已解决,就不上图了…...
鄂尔多斯教育网站入口/小程序平台
安装上sqlserver2005以后,打开程序时总报这个错误,后来解决了,但是现在学生都有这个问题,觉的有必要拿出来讲一下了: 试用vs打开一个决绝方案时出错,如下图所示: 产生这个的原因其实也很简单&am…...
做水果网站用什么域名/杭州网站设计公司
一、首先需具有一台外网ip的服务器 1.打开 /etc/ssh/sshd_config,将GatewayPorts参数设为yes 2.systemctl restart sshd.service,重启ssh服务 注意:较低版本的Linux服务器 重启命令为 service sshd restart 二、内网电脑 1.命令:ssh -NTf -…...
便宜的广州网站建设服务/杭州百度推广电话
【.NET面试题】什么是Hash,Hash的常见应用 知识储备 哈希(也叫散列)是一种查找算法(可用于插入),哈希算法希望能做到不经过任何比较(发生冲突,还是需要少许比较)&#…...
远程教育网站建设方案/产品运营主要做什么
目录 1、简介 2、使用 示例1(简单版) 示例2(复杂版) 示例3...
给介绍个网站黄色的/cps推广平台有哪些
废话不说,有篇论文可供参考:杨思雨:《伸展树的基本操作与应用》 Splay的好处可以快速分裂和合并。 14.07.26更新 实在看不惯那充满bug的指针树了!动不动就re!动不动就re!调试调个老半天,谁有好的…...
东莞做网站公司排名/seo优化快速排名技术
Windows 10 内置了一款 3D 应用 View 3D,可查看和打印 3D 模型,还能将 3D 对象显示到摄像头实时取景的现实世界。 可以打开.obj 格式文件。...