阅读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
创建新的系统调用trace来控制跟踪。它有一个参数,即整数掩码(mask),位数指定跟踪的那个系统调用。
eg. trace(1 << SYS_fork),跟踪fork调用,其中SYS_fork是系统调用号,位于kernel / syscall.h中 。 。
如果在掩码中设置了系统调用的编号,则必须修改xv6内核以在每个系统调用即将返回时打印出一行。
该行应包含pid,系统调用name和返回值。
无需打印系统调用参数。 trace系统调用应该跟踪调用它的进程和进程fork的孩子,但应该不会影响其他进程。
//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;
}
添加系统调用sysinfo,收集正在运行的系统的信息。
一个参数:指向结构sysinfo的指针(参阅kernel/sysinfo.h)
内核应填写此结构的字段:freemem字段设置为可用内存的字节数,nproc字段设置为状态为UNUSED的进程数。
//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);