Lab: system calls¶
System call tracing¶
assignment要求添加一个系统的trace功能
trace 32 grep hello README
接受一个mask参数,根据mask的对应bit指定要跟踪的syscall
添加声明¶
# user/usys.pl
# add
entry("trace");
// user/user.h
int trace(int);
// kernel/syscall.h
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
#define SYS_trace 22 // +
// kernel/syscall.c
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
extern uint64 sys_trace(void); // +
static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
@@ -127,8 +128,36 @@ static uint64 (*syscalls[])(void) = {
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
[SYS_trace] sys_trace, // +
};
syscall函数修改、实现sysproc.c中的函数¶
//kernel/syscall.c
void
syscall(void)
{
int num;
struct proc *p = myproc();
num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
p->trapframe->a0 = syscalls[num](); // 进行系统调用
| uint64 ret_val = p->trapframe->a0; // 获取系统调用返回值
| if((p->trace_mask>>num)&1) {
| printf("%d: syscall %s -> %d\n",p->pid, syscall_names[num], ret_val);
| }
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
对系统调用信息进行处理。
//kernel/syscall.c
// 添加系统调用名称数组
const char *syscall_names[] = {
[SYS_fork] "fork",
[SYS_exit] "exit",
[SYS_wait] "wait",
[SYS_pipe] "pipe",
[SYS_read] "read",
[SYS_kill] "kill",
[SYS_exec] "exec",
[SYS_fstat] "fstat",
[SYS_chdir] "chdir",
[SYS_dup] "dup",
[SYS_getpid] "getpid",
[SYS_sbrk] "sbrk",
[SYS_sleep] "sleep",
[SYS_uptime] "uptime",
[SYS_open] "open",
[SYS_write] "write",
[SYS_mknod] "mknod",
[SYS_unlink] "unlink",
[SYS_link] "link",
[SYS_mkdir] "mkdir",
[SYS_close] "close",
[SYS_trace] "trace",
};
// kernel/sysproc.c
uint64
sys_trace(void)
{
int mask;
if(argint(0, &mask) < 0) //从用户空间获取系统调用参数(实现位于kernel/syscall.c)
return -1;
myproc()->trace_mask = mask;
return 0;
}
获取系统调用参数,并保存到proc结构体中
// kernel/proc.h
// Per-process state
struct proc {
struct spinlock lock;
@---------------------------------------------@
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
int trace_mask; // + 在进程的struct中添加一个存储mask的变量
};
多进程trace¶
// kernel/proc.c
safestrcpy(np->name, p->name, sizeof(p->name));
np->trace_mask = p->trace_mask; // + 使 子进程的mask 等于 父进程的mask
pid = np->pid;
release(&np->lock);
Sysinfo¶
添加一个系统调用:sysinfo用于收集系统的信息
// kernel/sysinfo.h
struct sysinfo {
uint64 freemem; // amount of free memory (bytes)
uint64 nproc; // number of process
};
可以知道需要收集以下信息: 1. 进程数量 2. 空的memory
相关声明步骤与trace一致
获取进程数量¶
// kernel/proc.c
// Print a process listing to console. For debugging.
// Runs when user types ^P on console.
// No lock to avoid wedging a stuck machine further.
void
procdump(void)
{
static char *states[] = {
[UNUSED] "unused",
[SLEEPING] "sleep ",
[RUNNABLE] "runble",
[RUNNING] "run ",
[ZOMBIE] "zombie"
};
struct proc *p;
char *state;
printf("\n");
for(p = proc; p < &proc[NPROC]; p++){
if(p->state == UNUSED)
continue;
if(p->state >= 0 && p->state < NELEM(states) && states[p->state])
state = states[p->state];
else
state = "???";
printf("%d %s %s", p->pid, state, p->name);
printf("\n");
}
}
// +++++
uint64 num_proc() {
uint64 cnt = 0;
struct proc *p;
for(p = proc; p <&proc[NPROC];p++){
if(p->state == UNUSED) // 处于UNUSED状态就跳过
continue;
cnt++;
}
return cnt;
}
// +++++
观察可以发现,前一个函数就是一个输出所有运行进程的函数。 因而可以以相同的方式统计进程的数量。
获取空的memory大小¶
// Allocate one 4096-byte page of physical memory.
// Returns a pointer that the kernel can use.
// Returns 0 if the memory cannot be allocated.
void *
kalloc(void)
{
struct run *r;
acquire(&kmem.lock);
r = kmem.freelist;
if(r)
kmem.freelist = r->next;
release(&kmem.lock);
if(r)
memset((char*)r, 5, PGSIZE); // fill with junk
return (void*)r;
}
// +++++
uint64 num_freemem() {
struct run *r;
uint64 mem_bytes = 0;
r = kmem.freelist;
while (r) {
mem_bytes += PGSIZE;
r = r->next;
}
return mem_bytes;
}
// +++++
前一个函数是用于分配memory的,可以作为参照
将sysinfo的结构体复制到user space¶
需要参照sys_fstat() (kernel/sysfile.c) and filestat() (kernel/file.c)来使用copyout
// kernel/sysfile.c
uint64
sys_fstat(void)
{
struct file *f;
uint64 st; // user pointer to struct stat
if(argfd(0, 0, &f) < 0 || argaddr(1, &st) < 0)
return -1;
return filestat(f, st);
}
// kernel/file.c
// Get metadata about file f.
// addr is a user virtual address, pointing to a struct stat.
int
filestat(struct file *f, uint64 addr)
{
struct proc *p = myproc();
struct stat st;
if(f->type == FD_INODE || f->type == FD_DEVICE){
ilock(f->ip);
stati(f->ip, &st);
iunlock(f->ip);
if(copyout(p->pagetable, addr, (char *)&st, sizeof(st)) < 0)
return -1;
return 0;
}
return -1;
}
// kernel/sysproc.c
uint64
sys_sysinfo(void)
{
uint64 addr;
if(argaddr(0, &addr) < 0) // 获得addr
return -1;
struct sysinfo info; // 开头要加上#include "sysinfo.h"
info.freemem = num_freemem(); // memory空间
info.nproc = num_proc(); // 进程数量
if(copyout(myproc()->pagetable, addr, (char *)&info, sizeof(info)) <0) {
return -1;
}
return 0;
}