Using gdb

如果不知道怎么运行gdb的,要先看提供的文档

1.首先在终端1中运行make qemu-gdb,结尾处会得到一个端口号,供终端2使用

image-20231015163310695

2.新起一个终端,运行riscv64-unknown-elf-gdb,会进入如下所示的界面

image-20231015163444155

3.输入target remote localhost: 25501,然后再输入file kernel/kernel,就可以跟着指示往下做了

image-20231015163657010

问题1:Looking at the backtrace output, which function called syscall?

usertrap()

image-20231015163802691

问题2:What is the value of p->trapframe->a7 and what does that value represent?

initcode.S文件中看到a7保存的是要执行的系统调用号,从syscall.h中看到7SYS_exec

7和exec的系统调用号

问题3:What was the previous mode that the CPU was in?

通过输入p /x $sstatus,得到0x200000022,通过查询相关文档得到之前的特权级别是user mode,其中的8这个位置SPP就表示源自什么模式(0表示用户模式)。

image-20231015164751145

用户模式

问题4:Write down the assembly instruction the kernel is panicing at. Which register corresponds to the variable num?

跟着作业的指示走,在kernel.asm文件中搜索地址可以看到:

image-20231015200533042

在lw a3,0(zero)处panic,存在寄存器a3中

问题5: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?

内核因为加载了一个未使用的地址 0 处的内存数据而崩溃(Load page fault)。地址 0 并不映射到内核空间中(从 0x80000000 开始)。

问题6:What is the name of the binary that was running when the kernel paniced? What is its process id (pid)?

这个二进制的名字为 initcode ,其 process id 为 1.

System call tracing

建议先读几遍作业文档,这两个作业需要实现的逻辑不难,但是这个流程稍微多了些

  1. 首先在Makefile中添加$U/_trace\

image-20231016215601273

  1. 然后在user.h文件中注册对应的函数

  1. usys.pl文件中也添加对应的入口

image-20231016215841421

  1. syscall.h文件中添加对应的系统调用号

image-20231016220127159

  1. sysproc.c文件中实现sys_trace函数,从用户态获取到用户输入的mask,然后赋值给进程的syscall_trace成员,该成员需要在proc.h文件中的proc结构体添加

image-20231016221346681

image-20231016220431358

  1. proc.c文件中修改fork函数,在父进程创建子进程的时候将该值复制过去,如下

image-20231016220616830

  1. 最后修改syscall.c文件中的syscall函数,在进行系统调用的时候根据mask打印相应的信息

image-20231016220722966

Sysinfo

该实验流程跟前面的差不多,这里主要描述一下freememnproc怎么计算得到的,根据课程的作业提示我们在kalloc.c文件中创建一个count_freeMem用来返回空闲的内存字节数,至于为什么是这么计算,因为可以看出来xv6系统使用的是空闲链表法来分配内存的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 返回空闲内存的字节数=空闲页数*每页字节数
uint64 count_freeMem(void) {
// 先上锁
acquire(&kmem.lock);
uint64 bytes = 0;
struct run *r = kmem.freelist;
while (r) {
bytes += PGSIZE;
r = r->next;
}
// 释放锁
release(&kmem.lock);
return bytes;
}

并且在proc.c文件中创建一个count_process用来返回不是UNUSED状态的进程数量

1
2
3
4
5
6
7
8
9
10
// 返回已经分配的进程数
uint64 count_process(void) {
uint64 cnt = 0;
for (struct proc *p = proc; p < &proc[NPROC]; p++) {
// 只有读请求,不需要上锁
if (p->state != UNUSED)
cnt++;
}
return cnt;
}

这两个函数也都很直观,应该是没什么难度,最后跑一下make grade看看得分吧~

image-20231016221308188