Lab2怎么说呢,总觉得挺奇怪的,第一次接触系统调用代码,有点小懵,一个trace就做了我1个多小时。
lab1的时候只添加函数还是挺友好的,就跟平常写代码一样,这个系统调用就绕来绕去的,贼麻烦,也算体验到工程代码的<<乐趣>>?
不过还是得称赞一下lab2得Hints,没有它感觉根本做不下去:::
System call tracing(moderate)
首先看一下Hints,先pass掉那些test用例,后面再看:
lab2提示and我的提示:
在Makefile 的UPROGS 中添加$U/_trace
这一步比较简单,就像lab1那样在makefile里面添加即可
运行make qemu
,您将看到编译器无法编译user/trace.c ,因为系统调用的用户空间存根还不存在:将系统调用的原型添加到user/user.h ,存根添加到user/usys.pl ,以及将系统调用编号添加到kernel/syscall.h ,Makefile 调用perl脚本user/usys.pl ,它生成实际的系统调用存根user/usys.S ,这个文件中的汇编代码使用RISC-V的ecall
指令转换到内核。一旦修复了编译问题(注:如果编译还未通过,尝试先 *make clean*
,再执行 *make qemu*
),就运行trace 32 grep hello README
;但由于您还没有在内核中实现系统调用,执行将失败。
首先到user/user.h添加一个 int trace(int); (仿照上面那些,看着就知道要干嘛了
然后到user/usys.pl添加存根 entry("trace");
最后到kernel/syscall.h添加 #define SYS_trace 22
编译完可以看到user/usys.S里面新增了
Copy trace:
li a7, SYS_trace
ecall
ret
在kernel/sysproc.c 中添加一个sys_trace()
函数,它通过将参数保存到proc
结构体(请参见kernel/proc.h )里的一个新变量中来实现新的系统调用。从用户空间检索系统调用参数的函数在kernel/syscall.c 中,您可以在kernel/sysproc.c 中看到它们的使用示例。
修改fork()
(请参阅kernel/proc.c )将跟踪掩码从父进程复制到子进程。
修改kernel/syscall.c 中的syscall()
函数以打印跟踪输出。您将需要添加一个系统调用名称数组以建立索引。
跟着上面到第二步就可以 make qemu 进去看看了,然后输一下测试用例试试,就会出现 trace fail
接下来就是 三四五 这三步来真正写代码
第三步主要是实现sys_trace 函数:
首先需要按照提示的:它通过将参数保存到proc
结构体(请参见kernel/proc.h )里的一个新变量中来实现新的系统调用。可以知道,需要在 proc.h里面的结构体添加个新变量:
Copy // 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 traceMask; // Parameters needed for trace
};
然后按照提示就可以继续在 kernel/sysproc.c 里面添加一个 sys_trace 函数:
Copy uint64
sys_trace(void)
{
int trace;
// 获取整数参数
// argint:Fetch the nth 32-bit system call argument.
if (argint(0, &trace) < 0)
return -1;
// printf("%d\n",trace); // debug
myproc()->traceMask = trace;
return 0;
}
这个函数的实现就是说如果系统调用 sys_trace called,就将 traceMask 赋值为 命令行带进来的参数,如:
Copy trace 32 grep hello README // 这个就将 traceMask 赋值为32
trace 2147483647 grep hello README // // 这个就将 traceMask 赋值为 2147483647
然后到第四步,在kernel/proc.c的 fork() 加一行语句将 跟踪掩码从父进程复制到子进程:
Copy int
fork(void)
{
...
// trace to child
np->traceMask = p->traceMask;
...
}
第五步:修改kernel/syscall.c 中的syscall()
函数以打印跟踪输出,按照提示需要先建立一个名称索引。(wc,第一次见到这种写法,属实牛皮
Copy // hints:修改kernel/syscall.c中的syscall()函数以打印跟踪输出。您将需要添加一个系统调用名称数组以建立索引。
static char* syscallsName[] = {
[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",
};
最后就是要修改 syscall()函数来输出了
Copy 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]();
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
p->trapframe->a0 = -1;
}
// if(num == SYS_trace) {
// printf("trace\n");
// }
// 按题目给的三个案例可以推断出这里是要按位与的,像第一个给32,
// 只输出traceMask==32的。
// 第二个说是:2147483647将所有31个低位置为1。然后所有都输出了,因此需要用 &
// 先用 1<<num 转一下,因为题目就这样要求
// xv6 4.3里面说 p->trapframe->a0 存储的是 syscall的返回值,上面那个if也可以看出来
if (p->traceMask & (1 << num)) {
printf("%d: syscall %s -> %d\n",p->pid,syscallsName[num],p->trapframe->a0);
}
}
finally : 有个通过的小细节啊,上面的printf中间全要用空格,我刚开始用 tab,test还给我报了三个错....
Copy == Test trace 32 grep == trace 32 grep: OK (1.3s)
(Old xv6.out.trace_32_grep failure log removed)
== Test trace all grep == trace all grep: OK (1.0s)
(Old xv6.out.trace_all_grep failure log removed)
== Test trace nothing == trace nothing: OK (0.9s)
== Test trace children == trace children: OK (11.8s)
(Old xv6.out.trace_children failure log removed)
最后我自己总结一下调用的,因为写完这文章再回顾还是得继续看 Hints:
Makefile 调用perl脚本user/usys.pl ,它生成实际的系统调用存根user/usys.S ->
sh 调用 trace 指令 ->
trace.c 将 argv[1] 传入 user/user.h 的 int trace(int) ->
user/user.h的存根在user/usys.pl 的 entry("trace"); 进入系统调用 ->
syscall()在if语句里面进入kernel/sysproc.c中,执行 sys_trace(),将argv[1]添加到myproc()->traceMask中 ->
进程状态就被trace标记上了,然后转回trace.c,执行exec ->
在exec源码中这个 struct proc *p = myproc();是带有traceMask的 ->
然后一执行syscall就会被判断到,然后输出我们想要跟踪的状态,如果是fork了的话,也会通过我们在fork里面给子进程添加的traceMask进而跟踪子进程
可能总结的不对,之后学明白了再回来看看需不需要改
Sysinfo(moderate)
在Makefile add $U/_sysinfotest\
在user.h add
Copy struct sysinfo;
int sysinfo(struct sysinfo *);
在usys.pl add entry("sysinfo");
在kernel/syscall.h添加 #define SYS_sysinfo 23
在sysproc.c添加:
Copy // 写法参照 sys_fstat()(kernel/sysfile.c)和filestat()(kernel/file.c)
uint64
sys_sysinfo(void)
{
struct sysinfo s;
// 因为sysinfotest里面传过来的是 struct sysinfo *;所以拿地址就行
uint64 addr;
if (argaddr(0,&addr) < 0) {
return -1;
}
// get free memory size
s.freemem = getfreem();
// get process numbers: state != unused
s.nproc = getprocinfo();
// 使用copyout将数据传回给用户
struct proc *p = myproc();
if (copyout(p->pagetable,addr,(char *)&s,sizeof(s))<0) {
return -1;
}
return 0;
}
在defs.h的不同位置添加上面两个函数,根据文件的注释就行
在kalloc.c添加:
我最开始是直接count++,发现test的时候错了,报的那个正确数是我得到的 4096倍,因此发现了PGSIZE的含义: #define PGSIZE 4096 // bytes per page
Copy // 获取空闲内存量
int getfreem() {
struct run *r;
// 我猜kmem存放的应该就是页的集合
// freelist 就是空闲列表,存放空闲内存地址,在ostep 15章有介绍
r = kmem.freelist;
int count = 0;
while (r)
{
count+=PGSIZE;
r = r->next;
}
return count;
}
在proc.c添加:
Copy int
getprocinfo()
{
struct proc *p;
int count = 0;
// NPROC 是最大进程数
for(p = proc;p < &proc[NPROC];p++) {
if(p->state != UNUSED) count++;
}
return count;
}
其他一些需要的添加跟上面一样,如果要使用trace debug的话参照我上面的添加就行,感觉trace还是写的很鸡肋,在我debug的过程中没有产生任何作用,还得是老师的测试脚本