Appearance
system calls
开启新实验
git fetch
git checkout syscall
make cleanSystem call tracing
任务描述:接受一个参数mask指定需要追踪的系统调用,当指定的系统调用即将返回时打印一行信息(PID、该调用的名称、返回值),要追踪调用该命令的过程以及其后fork的所有子进程。
user/trace.c已经完成了,修改Makefile,把$U/_trace\添加到UPROGS。在用户态的头文件
user/user.h注册trace这一user function使得用户态程序可以找到这个跳板入口函数,int trace(int);。在
user/usys.pl注册一个ecall到kernel的kernel态的trace的入口entry("trace");,这个脚本在运行后会生成 usys.S 汇编文件,里面定义了每个 system call 的用户态跳板函数。修改
kernel/proc.h增加新变量,int mask;。修改
kernel/sysproc.c,增加sys_trace()方法,把参数写入proc,获得参数的方法在kernel/syscall.c中,使用argint。

uint64 sys_trace(void)
{
int mask;
if(argint(0, &mask) < 0)
return -1;
myproc() -> mask = mask;
return 0;
}在
kernel/syscall.h添加#define SYS_trace 22。修改
kernel/syscall.c,用 extern 全局声明新的内核调用函数,并且在 syscalls 映射表中,加入从前面定义的编号到系统调用函数指针的映射。
extern uint64 sys_trace(void);
static uint64 (*syscalls[])(void) = {
...
[SYS_trace] sys_trace,
}这里 [SYS_trace] sys_trace 是 C 语言数组的一个语法,表示以方括号内的值作为元素下标。比如 int arr[] = {[3] 2333, [6] 6666} 代表 arr 的下标 3 的元素为 2333,下标 6 的元素为 6666,其他元素填充 0 的数组。(该语法在 C++ 中已不可用)
- 修改
kernel/proc.c的fork()方法,把mask从父进程拷贝到子进程。
np->mask = p->mask;- 所有的系统调用到达内核态后,都会进入到
syscall()这个函数进行处理,所以要跟踪所有的内核函数,只需要修改kernel/syscall.c的syscall()方法,实现最后的打印输出。
void syscall(void)
{
int num;
struct proc *p = myproc();
char* syscall_name[22] = {"fork", "exit", "wait", "pipe", "read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime", "open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace"};
num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]){
p->trapframe->a0 = syscalls[num]();
if((p->mask >> num) & 1)
printf("%d: syscall %s -> %d\n", p->pid, syscall_name[num-1], p->trapframe->a0);
} else {
printf("%d %s:unknown sys call %d\n", p->pid, p->name, num);
p->trapframe->a0 =-1;
}
}Sysinfo
kernel/sysinfo.h

任务描述:写一个sysinfo这个system call,需要获取当前空闲的内存大小填入struct sysinfo.freemem中,获取当前所有不是UNUSED的进程数量填入struct sysinfo.nproc中,并将这个struct从内核空间传递到用户空间。
user/sysinfotest.c已经完成了,修改Makefile,把$U/_sysinfotest\添加到到UPROGS修改用户态的头文件
user/user.h
struct sysinfo;
int sysinfo(struct sysinfo *);在
user/usys.pl添加entry("sysinfo");在
kernel/kalloc.c添加计算空闲内存的函数freemem()
统计空闲页数,乘上页大小 PGSIZE 就是空闲的内存字节数。
uint64 freemem(void)
{
acquire(&kmem.lock); // 锁
uint64 mem_bytes = 0;
struct run *r = kmem.freelist; // 获取空闲页链表的根节点
while(r){ // 统计空闲页数
mem_bytes += PGSIZE;
r = r->next;
}
release(&kmem.lock); // 释放
return mem_bytes;
}- 在
kernel/proc.c添加获取运行的进程数的函数nproc()
遍历proc,统计状态不为UNUSED的个数。
uint64 nproc(void)
{
uint64 cnt = 0;
for(int i = 0; i < NPROC; i++){
if(proc[i].state != UNUSED) cnt++;
}
return cnt;
}- 在内核的头文件
kernel/defs.h中添加这两个函数的声明
uint64 freemem(void);
uint64 nproc(void);- 在
kernel/sysproc.c增加系统调用sys_sysinfo(),将填写好freemem和nproc的sysinfo写到用户空间。记得添加头文件sysinfo.h(不然会报错不知道大小)
使用到了copyout实现将数据从内核空间复制到用户空间。
uint64 sys_sysinfo(void)
{
uint64 addr;
if(argaddr(0, &addr) < 0)
return -1;
struct sysinfo sinfo;
sinfo.freemem = freemem();
sinfo.nproc = nproc();
if(copyout(myproc()->pagetable, addr, (char *)&sinfo, sizeof(sinfo)) < 0)
return -1;
return 0;
}在
kernel/syscall.h添加#define SYS_sysinfo 23修改
kernel/syscall.c
extern uint64 sys_sysinfo(void);
static uint64 (*syscalls[])(void) = {
...
[SYS_sysinfo] sys_sysinfo,
}The End

还是感觉乱乱的,跟着hint一点一点做。
这个Lab主要是在了解系统调用的流程,以及开始尝试获取一些进程的相关信息。