Lab 2: system calls
环境配置
将xv6 代码切换到syscall 分支并初始化
1 | $ git fetch |
一、trace 系统调用(难度:moderate)
添加一个trace system call调用,可以实现跟踪system call。此函数入参为一个数字,可以控制跟踪哪些system call。如:
trace(1<<SYS_fork),trace(10b),trace(2)表示跟踪fork调用;
trace(1<<SYS_read),trace(10 0000b),trace(32)表示跟踪read调用;
trace(10 0010b),trace(34)表示跟踪fork、read调用;
1、系统调用流程
用户态中:
- 在
user/user.h做函数声明 Makefile调用usys.pl(perl脚本)生成usys.S,里面写了具体实现,通过ecall进入 kernel,通过设置寄存器a7的值,表明调用哪个system callecall执行后存储用户态上下文信息到进程的trapframe结构体中,并加载内核态页表、寄存器等信息
内核态中:
- 开始执行
syscall函数,通过读取trapframe中a7寄存器的值,判断系统调用类型 - 通过函数指针执行对应内核函数,实现系统调用功能
2、具体实现
- 在
user/user.h中申明sys_trace函数1
int trace(int);
- 在
user/usys.pl中定义函数 entry1
entry("trace");
- 在
Makefile中添加$U/_trace - 在
kernel/syscall.h中添加系统调用号宏定义1
- 在
kernel/proc.h的proc结构体中增加mask数组1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25struct proc
{
struct spinlock lock;
// p->lock must be held when using these:
enum procstate state; // Process state
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
int xstate; // Exit status to be returned to parent's wait
int pid; // Process ID
// wait_lock must be held when using this:
struct proc *parent; // Parent process
// these are private to the process, so p->lock need not be held.
uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
struct trapframe *trapframe; // data page for trampoline.S
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
char mask[32]; // Trace mask
}; - 在
kernel/sysproc.c新增函数sys_trace,将系统调用号存入到proc结构体的mask数组中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22uint64 sys_trace(void)
{
int n;
if (argint(0, &n) < 0)
return -1;
struct proc *p = myproc();
char *mask = p->mask;
int i = 0;
while (n > 0 && i < 32)
{
if (n % 2)
{
mask[i++] = '1';
}
else
{
mask[i++] = '0';
}
n >>= 1;
}
return 0;
} - 在
kernel/syscall.c中引入sys_trace函数申明,并与系统调用号进行关联1
2
3
4extern uint64 sys_trace(void);
// 在 (*syscalls[])(void) 中添加以下内容
[SYS_trace] sys_trace, - 在
kernel/syscall.c中新建一个数组存放system_call的名称1
static char *syscall_names[32] = {"", "fork", "exit", "wait", "pipe", "read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime", "open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace"};
- 在
kernel/syscall.c的syscall函数中实现具体的跟踪功能,即判断当前系统调用号是否在需要跟踪的mask中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23void 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]();
// trace begin
if (strlen(p->mask) > 0 && p->mask[num] == '1')
{
printf("%d: syscall %s -> %d\n", p->pid, syscall_names[num], p->trapframe->a0);
}
// trace end
}
else
{
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
} - 在
kernel/proc.c的fork函数中实现自动复制父进程的mask1
2
3
4
5
6
7
8
9
10
11
12
13
14// Copy user memory from parent to child.
if (uvmcopy(p->pagetable, np->pagetable, p->sz) < 0)
{
freeproc(np);
release(&np->lock);
return -1;
}
np->sz = p->sz;
// Copy mask from parent to child
safestrcpy(np->mask, p->mask, sizeof(p->mask));
// copy saved user registers.
*(np->trapframe) = *(p->trapframe);
二、sysinfo 系统调用(难度:moderate)
添加一个 sysinfo system call 调用,可以实现打印可用空间(字节)、可用进程数。
1、具体实现
- 在
user/user.h中申明sysinfo结构体并申明sys_sysinfo函数1
2
3struct sysinfo;
int sysinfo(struct sysinfo *); - 在
user/usys.pl中定义函数 entry1
entry("sysinfo");
- 在
Makefile中添加$U/_sysinfotest - 在
kernel/syscall.h中添加系统调用号宏定义1
- 在
kernel/kalloc.c新增函数freemem_size用来统计可用空间大小1
2
3
4
5
6
7
8
9
10int freemem_size(void)
{
struct run *r;
int num = 0;
for (r = kmem.freelist; r; r = r->next)
{
num++;
}
return num * PGSIZE;
} - 在
kernel/proc.c新增函数proc_num用来统计可用进程数1
2
3
4
5
6
7
8
9
10
11int proc_num(void)
{
struct proc *p;
uint64 num = 0;
for (p = proc; p < &proc[NPROC]; p++)
{
if (p->state != UNUSED)
num++;
}
return num;
} - 在
kernel/defs.h中申明上面添加的两个函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30// kalloc.c
void *kalloc(void);
void kfree(void *);
void kinit(void);
int freemem_size(void); // 新增
// proc.c
int cpuid(void);
void exit(int);
int fork(void);
int growproc(int);
void proc_mapstacks(pagetable_t);
pagetable_t proc_pagetable(struct proc *);
void proc_freepagetable(pagetable_t, uint64);
int kill(int);
struct cpu *mycpu(void);
struct cpu *getmycpu(void);
struct proc *myproc();
void procinit(void);
void scheduler(void) __attribute__((noreturn));
void sched(void);
void sleep(void *, struct spinlock *);
void userinit(void);
int wait(uint64);
void wakeup(void *);
void yield(void);
int either_copyout(int user_dst, uint64 dst, void *src, uint64 len);
int either_copyin(void *dst, int user_src, uint64 src, uint64 len);
void procdump(void);
int proc_num(void); // 新增 - 在
kernel/sysproc.c引入sysinfo.h,并新增函数sys_sysinfo,调用上面的两个函数并将结果封装到sysinfo结构体中,使用copyout函数将结构体传回用户态1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
uint64 sys_sysinfo(void)
{
uint64 addr;
struct proc *p = myproc();
struct sysinfo info;
if (argaddr(0, &addr) < 0)
{
return -1;
}
info.freemem = freemem_size();
info.nproc = proc_num();
if (copyout(p->pagetable, addr, (char *)&info, sizeof(info)) < 0)
return -1;
return 0;
} - 在
kernel/syscall.c中引入sys_sysinfo函数申明,并与系统调用号进行关联1
2
3
4extern uint64 sys_sysinfo(void);
// 在 (*syscalls[])(void) 中添加以下内容
[SYS_sysinfo] sys_sysinfo, - 在
kernel/syscall.c的system_call中加入sysinfo1
static char *syscall_names[32] = {"", "fork", "exit", "wait", "pipe", "read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk", "sleep", "uptime", "open", "write", "mknod", "unlink", "link", "mkdir", "close", "trace", "sysinfo"};
实验结果
