Lab2 System Calls

开学!

实验开始前

1
2
3
git fetch
git checkout syscall
make clean

使用 gdb

  1. 哪个函数调用了 syscall
    • usertrap()
    • 在 syscall 设置断点后,输入 backtrace 查看栈回溯
  2. p->trapframe->a7 的值是多少,值代表什么?
    • 7,代表系统调用号 SYS_exec
    • 运行到 num = p->trapframe->a7,输入 p num
  3. CPU 的上一个模式是什么?
    • 给的文档看不懂。。
    • 更新:sstatus 的 SSP 位表示 trap 来自哪一位
  4. num = * (int *) 0;,kernel 在哪条汇编指令 panic,哪个寄存器对应变量 num
    • lw a3, 8(zero),a2(?)
    • 多次更改 num 值来调试
  5. 为什么内核崩溃了?在内核地址空间 0 地址有映射吗?上面的 scause 值是否证实这一点?
    • 因为尝试访问 0 地址,没有有效映射
  6. 当内核 panic 时进程的名字是什么?进程 pid 是多少?
    • “initcode”,1

有点过于复杂,没能理解

  • 访问到 0 地址时为什么会跳转到 kernelvec 中
  • scause、sepc、stval 的含义

更新:访问 0 发生了 trap,跳转到内核中处理内核 trap 的位置;scause 描述 trap,sepc 保存发生 trap 时 pc 的值,stval 保存 trap 的值

System call tracing

user/trace.c 已经写好了程序,只需要实现系统调用即可

  1. 先在 user/user.h 加上原型,在 user/usys.pl 加上 stub (存根),在 kernel/syscall.h 加上系统调用号
  2. kernel.c 的 proc 结构体加上一个新变量 tracemask
  3. kernel/sysproc.c 加上 sys_trace,设置当前进程的 trackmask
kernel/sysproc.c
1
2
3
4
5
6
7
8
9
uint64
sys_trace(int)
{
int mask;

argint(0, &mask);
myproc()->tracemask = mask;
return 0;
}
  1. 修改 kernel/proc.c 的 fork 函数,将父进程的 tracemask 传给子进程
kernel/proc.c
1
np->tracemask = p->tracemask;
  1. 修改 kernel/syscall.c 的 syscall 函数,如果是 tracemask 对应的系统调用号,就打应出来(里面还要添加一个字符串数组 syscallNames)
kernel/syscall.c
1
2
3
if(1<<num & p->tracemask) {
printf("%d: syscall %s -> %d\n", p->pid, syscallNames[num], p->trapframe->a0);
}

一个很简单的系统调用,仅仅是获取系统调用参数,然后将参数传给 proc->tracemask,就可以实现,但是能学到很多细节

Sysinfo

这里我们要使用 copyout,因为系统调用函数位处于内核模式,需要进程的页表和虚拟地址来查找用户进程中变量的物理位置(比如 sysinfo 结构体),然后将内核的数据复制给用户进程

kernel/sysproc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
uint64
sys_sysinfo(void)
{
uint64 si;
struct sysinfo info;

argaddr(0, &si);
if(!si) {
return -1;
}
info.freemem = get_freemem();
info.nproc = get_nproc();
if(copyout(myproc()->pagetable, si, (char*)&info, sizeof(info)) < 0) {
return -1;
}
return 0;
}

写 get_freemem 时,观察 kalloc 函数,直接从 kmem.freelist 取一页内存返回,可以推测 kmem.freelist 包含所有可用的内存

kernel/kalloc.c
1
2
3
4
5
6
7
8
9
10
11
uint64
get_freemem(void)
{
struct run *r;
uint64 n = 0;

for(r = kmem.freelist; r; r = r->next) {
n += 4096;
}
return n;
}

写 ger_nproc 时,观察 procinit 函数,在 proc[NPROC] 数据中遍历初始化,且其中含 state 变量

kernel/proc.c
1
2
3
4
5
6
7
8
9
10
11
12
uint64
get_nproc(void)
{
struct proc *p;
uint64 nproc = 0;
for(p = proc; p < &proc[NPROC]; p++) {
if(p->state != UNUSED) {
nproc++;
}
}
return nproc;
}

记得在 sysproc.c 引入 sysinfo.h,在 defs.h 加上 get_freemem 和 get_nproc

Optional challenge exercises

调用 trace 时打印出系统调用的参数

记录每个系统调用参数个数然后打印出来就好了

计算负载平均值并通过 sysinfo 导出

不是很懂

作者

Humoooor

发布于

2022-10-20

更新于

2022-11-17

许可协议

评论