XV6实验-Lab1 Syscalls

文章目录

  • EXERCISE 0
  • EXERCISE 1 System Call Tracing
    • 目的
    • 提示
  • EXERCISE 2 Sysinfo
    • 目的
    • 提示

EXERCISE 0

阅读xv6 book第二章和第四章4.3和4.4节以及相关代码,理解xv6内核和系统调用
用户空间代码位于 user / user.h 和 user / usys.pl中 。
内核空间代码为 kernel / syscall.h ,kernel / syscall.c。
进程相关的代码是 内核/ proc.h 和 内核/ proc.c 。

代码阅读
user.h
定义了系统调用的一系列函数
fork
exit
wait
pipe
write
read
close
kill
exec
open
mknod
unlink
fstat
link
mkdir
chdir
dup
getpid
sbrk
sleep
uptime

ulib.c
stat
strcpy
memmove
strchr
strcmp
fprintf
printf
gets
strlen
memset
malloc
free
atoi
memcmp
memcpy

usys.S
定义SYSCALL(name)的汇编代码

syscall.h
定义system call numbers1~21

syscall.c
fetchaddr获取当前进村的uint64地址
fetchstr
argraw
argint获取32bit 系统调用
argaddr获取一个指针,不检查合法性(在copyin/copyout中做)
argstr获取n字长的系统调用 复制进buf
定义一系列的系统调用函数
void syscall(void) 系统调用函数。

proc.h
为内核上下文开关保存的寄存器。
struct context{ra; sp; s0~s11}
struct cpu{proc; context; noff; intena}CPU状态

trapframe
/trampoline.S中陷阱处理代码的每进程数据。
//坐在一个页面本身就在trampoline页面的下面
//用户页表。在内核页表中没有特别映射。
//sscratch寄存器指向这里。
//trampoline.S中的uservec在trapframe中保存用户寄存器,
//然后从trapframe的//kernel_sp、kernel_hartid、kernel_satp,初始化寄存器,并跳到kernel_trap。
//在trampoline.S设置中的usertrapret()和userret
//trapframe的内核,从trapframe,切换到user page表,然后进入user space。
//trapframe包括被调用者保存的用户寄存器,如s0-s11,因为通过usertrapret()返回用户路径不会通过整个内核调用堆栈。

procstate{UNUSED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE}

struct proc{lock, state, parent, chan, killed, xstate, pid, kstack, sz, pagetable, trapframe, context, ofile, vwd, name}每个进程的状态

proc.c
cpus
proc
initproc
forkret
wakeup1
freeproc
procinit//启动时初始化proc table
cpuid
mycpu
myproc
allocpid
allocproc
freeproc
proc_pagetable
proc_freepagetable
userinit
growproc
fork
reparent
exit
wait
scheduler
sched
yield
forkret
sleep
wakeup
wakeup1
kill
either_copyout
either_copyin
procdump

EXERCISE 1 System Call Tracing

目的

创建新的系统调用trace来控制跟踪。它有一个参数,即整数掩码(mask),位数指定跟踪的那个系统调用。
eg. trace(1 << SYS_fork),跟踪fork调用,其中SYS_fork是系统调用号,位于kernel / syscall.h中 。 。
如果在掩码中设置了系统调用的编号,则必须修改xv6内核以在每个系统调用即将返回时打印出一行。
该行应包含pid,系统调用name和返回值。
无需打印系统调用参数。 trace系统调用应该跟踪调用它的进程和进程fork的孩子,但应该不会影响其他进程。

提示

  1. 添加到Makefile的UPROGS中
  2. 运行make qemu会发现不能编译trace.c,因为系统调用的用户空间不存在。天津一个原型系统调用到user/user.h,存根到user/usys.pl和syscall号kernel/syscall.h中。Makefile会调用perl脚本user/usys.pl,生成user/usys.S,该存根使用RISC-V ecall指令转换到内核。解决编译问题后,运行trace 32 grep hello README,会失败,因为还没有实现系统调用
  3. 在kernel/sysproc.c中添加一个sys_trace()函数,该函数通过记住新变量中的参数来实现新的系统调用。proc结构(参阅kernel/proc.h),从用户空间检索系统调用的函数位于kernel/syscall.c中,参阅kernel/sysproc.c获得用法示例。
  4. 修改fork()(参阅kernel/proc.c)以将trace的掩码从父进程复制进子进程中。
  5. 修改syscall.c的syscall()函数以打印trace的输出。需要添加一个系统调用名称数组来建立索引。
//Makefile
	$U/_trace\
//user.h
int trace(int);// added by me.
//usys.pl
entry("trace"); #added by me
//syscall.h
#define SYS_trace  22
//sysproc.c
uint64
sys_trace(void)
{
  int n;
  if(argint(0, &n) < 0)
    return -1;
  myproc()->tracemask = n;
  return 0;  // not reached
}
//proc.h
struct proc{
	...
	int tracemask;               // Tracemask added by me
}
//syscall.c
extern uint64 sys_trace(void);
static uint64 (*syscalls[])(void) = {
...
[SYS_trace]   sys_trace,
}
void
syscall(void)
{
	if(num>0&&num<NELEM(syscalls) && syscalls[num]) {
		...
		// trace print here
    	if((1 << num) != 0 && p->tracemask) {
      	printf("%d: syscall %s -> %d", p->pid, p->name, p->trapframe->a0);
    	}
    	// trace print pid, syscall name, return value
	}
}

//proc.c
int
fork(void)
{
	// copy tracemask from parent to child.
  np->tracemask  = p->tracemask; 
}


EXERCISE 2 Sysinfo

目的

添加系统调用sysinfo,收集正在运行的系统的信息。
一个参数:指向结构sysinfo的指针(参阅kernel/sysinfo.h)
内核应填写此结构的字段:freemem字段设置为可用内存的字节数,nproc字段设置为状态为UNUSED的进程数。

提示

  1. 添加到Makefile
  2. make qemu后同样无法编译,按照上个实验的方式添加系统调用sysinfo。在user/user.h中声明struct sysinfo;int sysinfo(struct sysinfo *);
  3. sysinfo需要复制结构sysinfo回用户空间,参阅sys_fstat()(kernel/sysfile.c)和filestat()(kernel/file.c)关于如何使用copyout()的示例
  4. 收集可用内存量,在kernel/kalloc.c中添加一个函数
  5. 收集进程数,在kernel/proc.c中添加一个函数
//Makefile
UPROGS=\
...
	$U/_sysinfotest\
//user.h
struct sysinfo;
int sysinfo(struct sysinfo*);// added by me.
//usys.pl
entry("sysinfo"); #added by me
//syscall.h
#define SYS_sysinfo 23  
//syscall.c
extern uint64 sys_sysinfo(void);
[SYS_sysinfo] sys_sysinfo,
"sysinfo",
//sysproc.c
uint64
sys_sysinfo(void)
{
  uint64 addr;
  if(argaddr(0, &addr) < 0)
    return -1;
  
  struct proc *p = myproc();
  struct sysinfo info;
  info.freemem = freemem();
  info.nproc = nproc();
  if(copyout(p->pagetable, addr, (char*)&info, sizeof(info)) < 0)
    return -1;
  return 0;
}
//kalloc.c
uint64
freemem(void)
{
  uint64 freemem = 0;
  struct run *r;
  acquire(&kmem.lock);
  for(r = kmem.freelist; r ; r = r->next){
    freemem +=PGSIZE;
  }
  release(&kmem.lock);
  return freemem;
}
//proc.c
uint64
nproc(void)
{
  struct proc *p;
  uint64 nproc = 0;
  for(p = proc ; p < &proc[NPROC]; p++){
    //acquire(&p->lock);
    if(p->state != UNUSED){
      nproc++;
    }
    //release(&p->lock);
  }
  return nproc;
}
//def.h
kalloc.c
uint64          freemem(void);
proc.c
uint64          nproc(void);

你可能感兴趣的:(XV6,操作系统)