实验准备
- 读
xv6 book
第2章,第4章的4.3节和4.4节 - 系统调用的用户空间代码:
user/user.h
,user/usys.pl
- 系统调用的内核空间代码:
kernel/syscall.h
,kernel/syscall.c
- 进程相关代码:
kernel/proc.h
,kernel/proc.c
切换分支
$ git fetch
$ git checkout syscall
$ make clean
SYstem call tracing
添加1个系统调用
trace
,辅助完成后续实验的调试。该系统调用传入1个参数:整数mask
,它的位用来指定要跟踪的系统调用
流程图
实验流程与代码
1.声明系统调用
在user/user.h
中,添加int trac(int)
// system calls
int fork(void);
int exit(int) __attribute__((noreturn));
int wait(int*);
int pipe(int*);
int write(int, const void*, int);
int read(int, void*, int);
int close(int);
int kill(int);
int exec(char*, char**);
int open(const char*, int);
int mknod(const char*, short, short);
int unlink(const char*);
int fstat(int fd, struct stat*);
int link(const char*, const char*);
int mkdir(const char*);
int chdir(const char*);
int dup(int);
int getpid(void);
char* sbrk(int);
int sleep(int);
int uptime(void);
int trace(int);
2.添加entry
在user/usys.pl
中,添加entry("trace")
entry("fork");
entry("exit");
entry("wait");
entry("pipe");
entry("read");
entry("write");
entry("close");
entry("kill");
entry("exec");
entry("open");
entry("mknod");
entry("unlink");
entry("fstat");
entry("link");
entry("mkdir");
entry("chdir");
entry("dup");
entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
entry("trace");
3.添加系统调用编号
在kernel/syscall.h
中,添加#define SYS_trace 22
// System call numbers
#define SYS_fork 1
#define SYS_exit 2
#define SYS_wait 3
#define SYS_pipe 4
#define SYS_read 5
#define SYS_kill 6
#define SYS_exec 7
#define SYS_fstat 8
#define SYS_chdir 9
#define SYS_dup 10
#define SYS_getpid 11
#define SYS_sbrk 12
#define SYS_sleep 13
#define SYS_uptime 14
#define SYS_open 15
#define SYS_write 16
#define SYS_mknod 17
#define SYS_unlink 18
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
#define SYS_trace 22
4.添加全局声明
在kernel/syscall.c
中,添加extern uint64 sys_trace(void);
extern uint64 sys_chdir(void);
extern uint64 sys_close(void);
extern uint64 sys_dup(void);
extern uint64 sys_exec(void);
extern uint64 sys_exit(void);
extern uint64 sys_fork(void);
extern uint64 sys_fstat(void);
extern uint64 sys_getpid(void);
extern uint64 sys_kill(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_mknod(void);
extern uint64 sys_open(void);
extern uint64 sys_pipe(void);
extern uint64 sys_read(void);
extern uint64 sys_sbrk(void);
extern uint64 sys_sleep(void);
extern uint64 sys_unlink(void);
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
extern uint64 sys_trace(void);
5.添加系统调用表
在kernel/syscall.c
中,添加[SYS_trace] sys_trace,
static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
[SYS_exit] sys_exit,
[SYS_wait] sys_wait,
[SYS_pipe] sys_pipe,
[SYS_read] sys_read,
[SYS_kill] sys_kill,
[SYS_exec] sys_exec,
[SYS_fstat] sys_fstat,
[SYS_chdir] sys_chdir,
[SYS_dup] sys_dup,
[SYS_getpid] sys_getpid,
[SYS_sbrk] sys_sbrk,
[SYS_sleep] sys_sleep,
[SYS_uptime] sys_uptime,
[SYS_open] sys_open,
[SYS_write] sys_write,
[SYS_mknod] sys_mknod,
[SYS_unlink] sys_unlink,
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
[SYS_trace] sys_trace,
};
6.实现trace的功能
添加系统调用追踪号
在kernel/proc.h
中,添加int mask
// Per-process state
struct proc {
struct spinlock lock;
// p->lock must be held when using these:
enum procstate state; // Process state
struct proc *parent; // Parent process
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
// 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)
int mask; // 添加一个系统调用的追踪号
};
实现sys_trace
//kernel/sysproc.c
uint64 sys_trace(void){
int n;
//argint()用于读取在a0-a5寄存器中传递的系统调用参数
if(argint(0, &n)<0){
return -1;
}
//myproc()函数获取当前进程的struct proc,即PCB
myproc()->mask=n;
return 0;
}
fork时,子进程与父进程有同样的mask
// kernel/proc.c
// Create a new process, copying the parent.
// Sets up child kernel stack to return as if from fork() system call.
int
fork(void)
{
int i, pid;
struct proc *np;
struct proc *p = myproc();
// Allocate process.
if((np = allocproc()) == 0){
return -1;
}
// 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;
np->parent = p;
// copy saved user registers.
*(np->trapframe) = *(p->trapframe);
// Cause fork to return 0 in the child.
np->trapframe->a0 = 0;
// increment reference counts on open file descriptors.
for(i = 0; i < NOFILE; i++)
if(p->ofile[i])
np->ofile[i] = filedup(p->ofile[i]);
np->cwd = idup(p->cwd);
safestrcpy(np->name, p->name, sizeof(p->name));
np->mask = p->mask; // 子进程的trace_mask = 父进程的trace_mask
pid = np->pid;
np->state = RUNNABLE;
release(&np->lock);
return pid;
}
输出trace
建立系统调用字典,方便寻找
// kernel/syscall.c
static char *syscall_names[] = {"", "fork", "exit", "wait", "pipe",
"read", "kill", "exec", "fstat", "chdir", "dup", "getpid", "sbrk",
"sleep", "uptime", "open", "write", "mknod", "unlink", "link",
"mkdir", "close", "trace"};
在syscall
中,输出trace
// 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]();
if(p->mask && ((p->mask >> num) & 1)){
printf("%d: syscall %s -> %d\n",p->pid, syscall_names[num], p->trapframe->a0);
}
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
}
添加到Makefile
将user/trace.c
添加到Makefile
$U/_trace
7.结果显示
-
make qemu
-
trace 32 grep hello RADEME
Sysinfo
加1个系统调用,
sysinfo
用来收集当前系统的相关信息。
sysinfo
传入1个参数:指向struct sysinfo
的指针(user/sysinfo.h);其中freemen表示空闲内存的字节数,nproc表示当前进程状态不是UNUSED的进程数;提供了1个测试程序:
sysinfotest
,如果打印"sysinfotest:OK",证明完成
实验流程与代码
1.声明系统调用与结构体
在user/user.h
中,声明系统调用和结构体
-
声明结构体
struct stat; struct rtcdate; struct sysinfo;
-
声明系统调用
// system calls int fork(void); int exit(int) __attribute__((noreturn)); int wait(int*); int pipe(int*); int write(int, const void*, int); int read(int, void*, int); int close(int); int kill(int); int exec(char*, char**); int open(const char*, int); int mknod(const char*, short, short); int unlink(const char*); int fstat(int fd, struct stat*); int link(const char*, const char*); int mkdir(const char*); int chdir(const char*); int dup(int); int getpid(void); char* sbrk(int); int sleep(int); int uptime(void); int trace(int); int sysinfo(struct sysinfo*);
2. 添加entry
在user/usys.pl
中,添加entry("sysinfo")
entry("fork");
entry("exit");
entry("wait");
entry("pipe");
entry("read");
entry("write");
entry("close");
entry("kill");
entry("exec");
entry("open");
entry("mknod");
entry("unlink");
entry("fstat");
entry("link");
entry("mkdir");
entry("chdir");
entry("dup");
entry("getpid");
entry("sbrk");
entry("sleep");
entry("uptime");
entry("trace");
entry("sysinfo");
3. 添加系统调用编号
在kernel/syscall.h
中,添加#define SYS_sysinfo 23
// System call numbers
#define SYS_fork 1
#define SYS_exit 2
#define SYS_wait 3
#define SYS_pipe 4
#define SYS_read 5
#define SYS_kill 6
#define SYS_exec 7
#define SYS_fstat 8
#define SYS_chdir 9
#define SYS_dup 10
#define SYS_getpid 11
#define SYS_sbrk 12
#define SYS_sleep 13
#define SYS_uptime 14
#define SYS_open 15
#define SYS_write 16
#define SYS_mknod 17
#define SYS_unlink 18
#define SYS_link 19
#define SYS_mkdir 20
#define SYS_close 21
#define SYS_trace 22
#define SYS_sysinfo 23
4. 添加全局声明
在kernel/syscall.c
中,添加extern uint64 sys_sysinfo(void);
extern uint64 sys_chdir(void);
extern uint64 sys_close(void);
extern uint64 sys_dup(void);
extern uint64 sys_exec(void);
extern uint64 sys_exit(void);
extern uint64 sys_fork(void);
extern uint64 sys_fstat(void);
extern uint64 sys_getpid(void);
extern uint64 sys_kill(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_mknod(void);
extern uint64 sys_open(void);
extern uint64 sys_pipe(void);
extern uint64 sys_read(void);
extern uint64 sys_sbrk(void);
extern uint64 sys_sleep(void);
extern uint64 sys_unlink(void);
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
extern uint64 sys_trace(void);
extern uint64 sys_sysinfo(void);
5. 添加系统调用表
在kernel/syscall.c
中,添加[SYS_trace] sys_trace,
static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
[SYS_exit] sys_exit,
[SYS_wait] sys_wait,
[SYS_pipe] sys_pipe,
[SYS_read] sys_read,
[SYS_kill] sys_kill,
[SYS_exec] sys_exec,
[SYS_fstat] sys_fstat,
[SYS_chdir] sys_chdir,
[SYS_dup] sys_dup,
[SYS_getpid] sys_getpid,
[SYS_sbrk] sys_sbrk,
[SYS_sleep] sys_sleep,
[SYS_uptime] sys_uptime,
[SYS_open] sys_open,
[SYS_write] sys_write,
[SYS_mknod] sys_mknod,
[SYS_unlink] sys_unlink,
[SYS_link] sys_link,
[SYS_mkdir] sys_mkdir,
[SYS_close] sys_close,
[SYS_trace] sys_trace,
[SYS_sysinfo] sys_sysinfo,
};
6.实现sysinfo的功能
获取空闲内存
根据提示4,可以先去看源码kernel/kalloc.c
该函数为xv6的为物理内存分配器,物理内存分页管理,每页有PGSIZE(4096)字节
struct run {
struct run *next;
};
struct {
struct spinlock lock;
struct run *freelist; // 指向空闲页的链表
} kmem;
void
kfree(void *pa)
{
struct run *r;
if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
panic("kfree");
// Fill with junk to catch dangling refs.
memset(pa, 1, PGSIZE);
r = (struct run*)pa;
//释放内存,将内存放到链表头节点——前插法
acquire(&kmem.lock);
r->next = kmem.freelist;
kmem.freelist = r;
release(&kmem.lock);
}
在kernel/kalloc.c
中添加cnt_free_mem
函数统计空闲内存
// 计算空闲内存
uint64 cnt_free_mem(void){
acquire(&kmem.lock); //上锁
uint64 free_mem_count = 0;
struct run *r = kmem.freelist;
while(r){
free_mem_count += PGSIZE;
r = r->next;
}
release(&kmem.lock);
return free_mem_count;
}
将该函数放入到kernel/defs.h
中声明
// kalloc.c
void* kalloc(void);
void kfree(void *);
void kinit(void);
uint64 cnt_free_mem(void);
获取运行中的线程数
根据提示4,可以先去看源码kernel/proc.c
struct proc proc[NPROC]; // 每个元素都代表1个进程的相关信息
在kernel/proc.h
中查看proc
结构体的定义
// Per-process state
struct proc {
struct spinlock lock;
// p->lock must be held when using these:
enum procstate state; // Process state
struct proc *parent; // Parent process
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
// 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)
int mask;
};
只需要遍历所有的进程,获取其状态信息,判断是不是UNUSED
并统计数目
在kernel/proc.c
中,添加cnt_proc
函数
// 计算进程数
uint64 cnt_proc(void){
uint64 cnt = 0;
for(struct proc *p = proc; p < &proc[NPROC]; p++){
if(p->state != UNUSED) cnt++;
}
return cnt;
}
将该函数放入到kernel/defs.h
中声明
// proc.c
int cpuid(void);
void exit(int);
int fork(void);
int growproc(int);
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 setproc(struct proc*);
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);
uint64 cnt_proc(void);
实现sys_sysinfo
#include "sysinfo.h"
uint64 sys_sysinfo(void){
// 从用户态读入1个指针,作为存放susinfo的buffer
uint64 addr;
if(argaddr(0, &addr) < 0)
return -1;
// 定义1个sysinfo结构的变量sinfo, 记录系统调用的信息
struct sysinfo sinfo;
sinfo.freemem = cnt_free_mem(); // 计算空闲的字节数
sinfo.nproc = cnt_proc(); // 计算并行的线程数
// 复制 sinfo 的内容到用户态传来的地址
// 使用copyout 结合当前进程的页表,获得进程传进来的指针(逻辑地址)对应的物理地址
// 将sinfo中的数据 复制到 该指针所指向的位置,共用户进程使用
if(copyout(myproc()->pagetable, addr, (char*)&sinfo, sizeof(sinfo)) < 0)
return -1;
return 0;
}
添加Makefile
$U/_sysinfotest
id procdump(void);
uint64 cnt_proc(void);
##### 实现sys_sysinfo
```c
#include "sysinfo.h"
uint64 sys_sysinfo(void){
// 从用户态读入1个指针,作为存放susinfo的buffer
uint64 addr;
if(argaddr(0, &addr) < 0)
return -1;
// 定义1个sysinfo结构的变量sinfo, 记录系统调用的信息
struct sysinfo sinfo;
sinfo.freemem = cnt_free_mem(); // 计算空闲的字节数
sinfo.nproc = cnt_proc(); // 计算并行的线程数
// 复制 sinfo 的内容到用户态传来的地址
// 使用copyout 结合当前进程的页表,获得进程传进来的指针(逻辑地址)对应的物理地址
// 将sinfo中的数据 复制到 该指针所指向的位置,共用户进程使用
if(copyout(myproc()->pagetable, addr, (char*)&sinfo, sizeof(sinfo)) < 0)
return -1;
return 0;
}
添加Makefile
$U/_sysinfotest