上一个Lab实现了一些shell工具,这个Lab实现一些系统调用,来帮助理解系统调用如何工作、如何暴露。
一、trace
1 问题分析
实现一个trace,能够追踪某个进程(包括子进程)指定系统调用执行情况,打印它的状态,如下图所示。
- 传递mask来表示追踪的是哪个系统调用,例如:mask=1<<SYS_fork。
- 打印进程及其子进程运行某个系统调用的日志,所以mask应该添加在进程结构体中,并且fork内核函数也需要copy mask字段。
主要步骤如下:
- kernel/proc.h添加tracemask字段。
- kernel/sysproc.c添加sys_trace函数。
- kernel/syscall.h添加系统调用号。
- kernel/syscall.c系统调用表中添加一项,并增加系统调用名称表。
- kernel/syscall.c中修改syscall函数。
- 在user/user.h中暴露该系统调用,在user/usys.pl中增加一项,用于生成汇编代码触发中断。
2 代码实现
代码语言:c复制//代码比较分散,只介绍主要代码
uint64
sys_trace(void)
{
int mask;
if(argint(0, &mask) < 0)
return -1;
myproc()->tracemask=mask;
return 0;
}
static char* syscallnames[] = {
[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",
};
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]();
if (p->tracemask&(1<<num)){
printf("%d: %s %s %s %dn",p->pid,"syscall",syscallnames[num],"->",p->trapframe->a0);
}
} else {
printf("%d %s: unknown sys call %dn",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
二、sysinfo
1 问题分析
sysinfo系统调用用来统计当前空闲物理内存、存在的进程数量,我们需要对内存分配器、进程管理有一定的了解。
- xv6的物理内存是用空闲链表管理的,统计这条链表上的页数就行。
- xv6的进程分配是采用一个进程数组来维护,直接遍历这个数组上非UNUSED状态的进程即可。
- 用户态向内核传递数据,会存放在寄存器中,而内核trap代码会将寄存器保存在进程trap frame中。内核中有argint、argaddr、argfd,支持读取整数、指针、文件描述符,都是argraw的封装。
- 内核态和用户态不会直接读写同一份数据,而是会拷贝,copyout是将数据拷贝到用户地址中。
2 内存分配和进程管理
代码语言:c复制struct run {
struct run *next;
};
struct {
struct spinlock lock;
struct run *freelist;
} kmem;
struct cpu cpus[NCPU];
//进程管理数组
struct proc proc[NPROC];
enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
每个run表示一个物理页,它的指针就是物理页起始地址,每一物理页都会存放下一物理页的地址,来减少辅助空间。进程是采用一个数组来管理。
3 代码实现
代码语言:c复制uint64
sys_sysinfo(void)
{
struct sysinfo info;
info.freemem=count_free_memory();
info.nproc=count_proc();
uint64 addr;
if(argaddr(0, &addr) < 0)
return -1;
if(copyout(myproc()->pagetable,addr,(char*)&info,sizeof info)<0){
return -1;
}
return 0;
}
uint64 count_free_memory(void){
int pageNum=0;
struct run *r;
acquire(&kmem.lock);
r=kmem.freelist;
while (r){
pageNum ;
r=r->next;
}
release(&kmem.lock);
return pageNum*PGSIZE;
}
//计算进程数量
uint64 count_proc(void){
int count=0;
for(int i=0;i< NPROC ;i ){
if(proc[i].state!=UNUSED){
count ;
}
}
return count;
}