6.1810-system calls

本文最后更新于:2024年3月5日 上午

接下来我们要开始看系统调用的真面目啦

Using gdb

这次算是我第一次认真gdb了,之前都是直接用vsc封装好的调试的

对于我的环境wsl ubuntu20.04来说这里还有一些小坑

需要启动riscv-gdb,设置架构,手动连接端口并加载符号表

qemu的端口号和kernel符号表的位置可能会有所不同(?,这里是chat老师的样例,不过用vsc remote的话端口号是好找的(

1
gdb-multiarch
1
2
3
(gdb) set architecture riscv:rv64
(gdb) target remote localhost:1234
(gdb) file path/to/your/kernel/kernel

不过反正是自学所以也没有ta查,就随便往txt里写了点

  1. Threads call usertrap()
1
2
3
(gdb) bt
#0 syscall () at kernel/syscall.c:133
#1 0x0000000080001d70 in usertrap () at kernel/trap.c:67
  1. reg a7 represent SYSCALL type
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(gdb) p/x*p
$1 = {lock = {locked = 0x0, name = 0x800081b8, cpu = 0x0},
state = 0x4, chan = 0x0, killed = 0x0, xstate = 0x0, pid = 0x1,
parent = 0x0, kstack = 0x3fffffd000, sz = 0x4000,
pagetable = 0x87f6c000, trapframe = 0x87f74000, context = {
ra = 0x800014c2, sp = 0x3fffffdd10, s0 = 0x3fffffdd40,
s1 = 0x80008d50, s2 = 0x80008920, s3 = 0x0, s4 = 0x80021d38,
s5 = 0xffffffffffffffff, s6 = 0x3f78, s7 = 0x930, s8 = 0x64,
s9 = 0x6c, s10 = 0x78, s11 = 0x70}, ofile = {0x80018a50,
0x80018a50, 0x80018a50, 0x0 <repeats 13 times>},
cwd = 0x80016e60, name = {0x69, 0x6e, 0x69, 0x74, 0x0, 0x0, 0x64,
0x65, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0}}

(gdb) p/x ((struct trapframe*)0x87f74000)->a7
$2 = 0x10
  1. sstatus means Supervisor status register , previous privilege is user mode
    SPP (Supervisor previous privilege) = 0
    SIE (Supervisor Interrupt Enable) = 1
1
2
(gdb) p /x $sstatus
$3 = 0x22
  1. so variable num in reg a3
    sepc=0x0000000080002050
    num = * (int *) 0;
    80002050: 00002683 lw a3,0(zero) # 0 <_entry-0x80000000>

  2. In xv6 , 0-0x1000 is Unused.
    It’s confirmed by scause:
    scause 0x000000000000000d means Load Page Fault

1
2
3
4
(gdb) p p->name
$1 = "initcode\000\000\000\000\000\000\000"
(gdb) p p->pid
$2 = 1

trace

通过掩码来追踪系统调用

这个掩码需要记录在进程信息中,所以在kernel/proc.h中的struct proc中添加

1
2
3
4
5
struct proc {
...
int trace_mask;
...
}

在内核的系统调用过程中(kernel/syscall.c)处理掩码

1
2
3
4
5
6
7
8
9
if (p->trace_mask !=0 ){
for (int i = 0; i < NELEM(syscalls); i++) {
if (p->trace_mask & (1 << i)) {
if (num == i) {
printf("%d: syscall %s -> %d\n", p->pid , syscallNames[num] , p->trapframe->a0);
}
}
}
}

根据已经处理好的用户态的trace实现,去补全sys_trace()函数

1
2
3
4
5
6
7
8
9
10
11
12
uint64
sys_trace(void)
{
int mask;
struct proc *p = myproc();

argint(0, &mask);

p->trace_mask = mask;

return 0;
}

注意使用argint向内核态传参

接下来确保子进程会继承追踪掩码,在kernel/proc.c中的fork函数中添加

1
2
3
4
5
6
7
int
fork(void)
{
...
np->trace_mask = p->trace_mask;
...
}

sysinfo

在内核态收集信息(进程和内存)之后将结构体复制到用户态即可。

遍历proc数组收集进程数量

1
2
3
4
5
6
7
8
9
10
11
int
proc_collect(void)
{
int count = 0;
struct proc *p;
for(p = proc; p < &proc[NPROC]; p++){
if(p->state != UNUSED)
count++;
}
return count;
}

遍历链表收集内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int
count_free_memory(void)
{
int free_memory = 0;
struct run *r;

acquire(&kmem.lock);
for(r = kmem.freelist; r ; r = r->next) {
free_memory += PGSIZE;
}
release(&kmem.lock);

return free_memory;
}

最后通过copyout将数据拷贝到内核态的相应地址

1
2
3
4
5
6
7
8
9
10
11
12
13
uint64
sys_sysinfo(void){
struct proc *p = myproc();
struct sysinfo kinfo;
uint64 pr;
kinfo.freemem = count_free_memory();
kinfo.nproc = proc_collect();
argaddr(0,&pr);
if (copyout(p->pagetable,pr,(char *)&kinfo,sizeof(kinfo)) < 0){
return -1;
}
return 0;
}

6.1810-system calls
http://tzr.icu/20240304/6-1810-system-calls/
发布于
2024年3月4日
更新于
2024年3月5日
许可协议