Appearance
system calls
开启新实验
git fetch
git checkout syscall
make clean
System 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主要是在了解系统调用的流程,以及开始尝试获取一些进程的相关信息。