【MIT 6.S081】Lab2: System Calls

MIT 6.S081的Lab2: System calls题解

System call tracing (moderate)

要求

添加一个系统调用追踪函数,有一个整数参数为mask,二进制指定需要追踪的系统调用(也许是多个),打印进程id:系统调用名->返回值

分析

添加系统调用的步骤

  • 即为用户空间添加该系统调用的入口:

  • /user/user.h中添加函数声明

  • /user/usys.pl中添加入口entry("xxx");

  • kernel/syscall.h中定义系统调用号

  • kernel/syscall.csyscalls函数指针数组中添加对应的函数,则可以根据头文件里的系统调用号调用相应函数

实现本题的系统调用

  • kernel/proc.h中为进程添加变量记录trace的系统调用编号

  • kernel/sysproc.c中添加一个sys_trace()函数

  • 修改kernel/proc.cfork()函数,将系统调用编号提供给子进程

  • kernel/sysproc.c中添加sys_trace(),从寄存器中获得参数(即想追踪的系统调用mask)设置到当前的进程

  • 修改kernel/syscall.c中的syscall函数打印trace的信息,这里我卡了一下没仔细看提示,一开始直接用了proc类中的name,后来发现那个是进程名不是系统调用名字,所以另开一个字符串数组放入系统调用的名字,用系统调用的序号直接找到它输出

可能用到的函数

  • int argint(int n, int *p)kernel/sysproc.c中的函数大都使用,意思是获取第n个寄存器的值,将指针p指向它的值(并非指向寄存器,这里值是由另一个函数返回来的)

  • myproc():返回一个proc类的指针,指向当前进程,可以用它来完成mask赋值

  • static uint64 argraw(int n):返回对应的寄存器内容(trap前)

实现

编辑Makefile文件,在UPROGS=\一栏添加如下内容:

1
$U/_trace\

/user/user.h添加系统调用函数声明(参数是int 可以从/user/trace.c中的调用看出来):

1
2
// system calls
int trace(int);

/user/usys.pl添加:

1
entry("trace");

kernel/syscall.h添加:

1
#define SYS_trace  22

kernel/syscall.csyscalls函数指针数组中补充内容:

1
2
3
4
5
extern uint64 sys_trace(void);
static uint64 (*syscalls[])(void) = {
...
[SYS_trace] sys_trace,
}

kernel/proc.h中为进程添加变量mask来记录(一旦有调用trace)提供的系统调用编号:

1
2
3
4
5
// Per-process state
struct proc {
...
uint mask; // Mask for trace
}

kernel/proc.c中修改fork()函数,将mask提供给子进程:

1
2
3
4
5
int fork(void) {
...
// set the same mask
np->mask = p->mask;
... }

kernel/sysproc.c中添加一个sys_trace()函数:

1
2
3
4
5
6
7
8
9
10
11
// set current process mask
uint64
sys_trace(void)
{
uint mask;

if(argint(0, &mask) < 0)
return -1;
myproc()->mask = mask;
return 0;
}

修改kernel/syscall.c,添加系统调用函数名的字符串数组,以及修改syscall函数打印trace的信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
static const char* sysname[] = { "",
"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) {
int num;
struct proc* p = myproc();

num = p->trapframe->a7; // current proc's system call numbers
if (num > 0 && num < NELEM(syscalls) && syscalls[num]) {
p->trapframe->a0 = syscalls[num](); // return value
if (p->mask & (1 << num)) { // current proc is the sysproc we trace
printf("%d: syscall %s -> %d\n", p->pid, sysname[num], p->trapframe->a0);
}
}
else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}

测试

输入命令:

1
sudo python3 grade-lab-syscall trace

Sysinfo (moderate)

要求

添加一个sysinfo系统调用,参数是一个kernel/sysinfo.h中的结构体sysinfo指针,系统调用补充指针指向这个结构体的字段内容

分析

sysinfo结构体

  • freemem:空闲内存的字节数

  • nproc:正在运行(state的内容不是UNUSED,proc的定义在/kernel/proc.h)的进程数

流程

  • 与第一个题目中的分析相同

可能用到的函数

  • 拷贝的过程

    • copyout(pagetable_t pagetable, uint64 dstva, char *src, uint64 len):从内核拷贝内容到用户层,从src拷贝len字节到虚拟地址dstva在页表pagetable指向的位置,成功返回 00 ,否则返回 1-1

    • 还需要用上一个题中syscall.c中的函数(argint拿整数,argaddr拿指针)

    • proc结构体里有页表信息

  • 获取空闲内存

    • kernel/kalloc.c中的kalloc()kmen有个空闲列表,空闲列表的大小应该是宏定义的PGSIZE,补充一个函数,用同样的写法统计空闲列表的个数,就得到结果了
  • 获取进程数:

    • kernel/proc.c中的static struct proc* allocproc(void):模仿这个函数中分配进程的过程进行统计

实现

编辑Makefile文件,在UPROGS=\一栏添加如下内容:

1
$U/_sysinfotest\

/user/user.h添加系统调用函数声明(文档有提示):

1
2
3
// system calls
struct sysinfo;
int sysinfo(struct sysinfo *);

/user/usys.pl添加:

1
entry("sysinfo");

kernel/syscall.h添加:

1
#define SYS_sysinfo     23

kernel/syscall.csyscalls函数指针数组和函数名数组中补充内容:

1
2
3
4
5
6
extern uint64 sys_sysinfo(void);
static uint64 (*syscalls[])(void) = {
...
[SYS_sysinfotest] sys_sysinfo,
}
static const char* sysname[] = {"", ..., "sysinfo" };

根据上述分析写出kernel/sysproc.c中的sys_sysinfo()

1
2
3
4
5
6
7
8
9
10
11
12
uint64 sys_sysinfo(void) {
uint64 dstva; // virtual address

if (argaddr(0, &dstva) < 0) // get virtual address dstva
return -1;

struct sysinfo info;
info.freemem = get_freemem(); // kalloc.c
info.nproc = get_nproc(); // proc.c

return copyout(myproc()->pagetable, dstva, (char*)&info, sizeof(info));
}

其中get_freemem()函数写在kernel/kalloc.c中,记得补充函数声明到defs.h头文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// in defs.h
// kalloc.c
...
uint64 get_freemem(void);

// in kalloc.c
uint64 get_freemem(void) {
struct run* r;
acquire(&kmem.lock); // lock
r = kmem.freelist;
uint64 cnt = 0; // count freelist
while (r) {
r = r->next;
cnt++;
}
release(&kmem.lock);
return cnt * PGSIZE;
}

其中get_nproc()函数写在kernel/proc.c中,记得补充函数声明到defs.h头文件中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// in defs.h
// proc.c
uint64 get_nproc(void);

// in proc.c
uint64 get_nproc(void) {
struct proc* p;
uint64 cnt = 0;
for (p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if (p->state != UNUSED)
cnt++;
release(&p->lock);
}
return cnt;
}

测试

输入命令:

1
sudo python3 grade-lab-syscall sysinfotest