关於 xv6 的环境架设,可以参考我之前写的这篇文章
6.S082 课程连结(我这里用的是 2021 的版本)
这篇文章是要写课程当中的 Lab syscall
1. 开始写题目前应该要先做的几件事
2. 题目叫我们阅读的程序码
user/user.h
, user/usys.pl
kernel/syscall.h
, kernel/syscall.c
kernel/proc.h
, kernel/proc.c
3. 程序实作
4. 参考资料
先把他看完对於写 lab 会有帮助的
如果你看的是 2020 的版本,可以直接点这个连结
但如果你看的是 2021 的版本了话,要点选这个 github 页面 下载程序码并且自己 make
出来才行
我个人觉得 xv6 book 写的不是那麽平易近人,很多时候他假设了读者有一定的基础知识,
所以我是建议在书中看到了什麽不懂的名词,就先去搞懂後再回来看会比较好,
像是 page table, virtual memory, risc-v 的架构,一些 C 语言语法等等,
虽然这样感好像很花时间,但反正在看其他的东西本身也是一种学习,
在 Lab syscall 要我们先读 chapter 2, 4.3, 4.4,
就先把他们读完再继续吧,读的时候也要搭配 source code 会比要好懂
user/user.h
, user/usys.pl
user/user.h
,是个 system call 的 prototype
/*.........*/
// 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 chdir(const char*)
为例,在 kernel 中,他并不会真的有
int chdir(const char*)
{
//***........***//
}
这种实作方式
而是在 make qemu
之後,Makefile
会执行 user/usys.pl
这个脚本程序,
他会制造出 user/usys.S
这个 risc-v 组语:
#include "kernel/syscall.h"
.global fork
fork:
li a7, SYS_fork
ecall
ret
.global exit
exit:
li a7, SYS_exit
ecall
ret
...
也就是在 user program 中,叫到了 fork()
这个 function 之後,他会跳到 fork:
这个 flag(symbol) 这里来继续执行
如果 user program 呼叫了 fork()
这个 function 会执行以下两件事:
li
(load immediate):把 fork 的编号 SYS_fork
(定义在 kernel/syscall.h
) 放进 register a7
里ecall
:从 user mode 进入到 supervisor mode (实际上就是改变了 CPU chip 里的一个 flag)并且进入到了 kernel/syscall.c
的 syscall()
syscall()
会根据 `````` register a7
的 system call 编号来帮你把 program counter 指向 system call 真的实做的 function(在 kernel/sysfile.c
或是 kernel/sysproc.c
等等)
这麽做的原因在於,system call 是个权限较高的行为,所以处理起来要比较小心才行
user program 只能把呼叫的 system call 编号放在 register a7
然後由 syscall() 来决定这个 user program 是否有资格执行这个 system call
kernel/syscall.h
, kernel/syscall.c
像前面有提到的,kernel/syscall.h
定义着 system call 的编号:
// 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
kernel/syscall.c
则定义着一些执行 system call 所需要用的 function ,
例如如何拿取 user program 传来的参数等等,这些在 xv6 book 写的很清楚
kernel/proc.h
, kernel/proc.c
在 kernel 中,纪录着各个 process 的资讯,而他使用的资料结构就是 kernel/proc.h
的 struct proc
这个 struct
值得多加注意一下
依照 Lab system call 的 Some hints 的指示,一步一步的做下去就好
而这个 lab 已经提供了 user program user/trace.c
但是这个 program 呼叫到的 system call trace(int)
还没有被实作出来
我们的目的就是要把他实作出来才行
Makefile
中,加入 $U/_trace
user/user.h
:// user/user.h
...
int trace(int);
...
user/usys.pl
:...
entry("trace");
kernel/syscall.h
...
#define SYS_trace 22
到了这里,已经可以用 make qemu
compile 成功了,
只不过 trace
这个 system call 还是还没实作出来,
目前只解决了以 user program 而言的 link 上的问题
kernel/proc.h
中,增加一个 variablestruct proc {
...
uint trace_mask;
};
kernel/sysproc.c
实作出 sys_trace()
...
uint64
sys_trace(void)
{
int n;
if(argint(0, &n) < 0)
return -1;
struct proc *p = myproc();
p->trace_mask = n;
return 0;
}
其实就只是把 proc
中的 trace_mask
给设为 user program 传过来的参数而已
但业就是因为他改变了 kernel 中的资讯,所以需要用更高等级的权限(supervisor mode)才可以
kernel/syscall.c
加入extern uint64 sys_trace(void);
static uint64 (*syscalls[])(void) = {
...
[SYS_trace] sys_trace,
};
kernel/proc.c
中的 fork()
fork 出新的 process 时,也要把 trace_mask
也复制过去才行int
fork(void)
{
...
// copy trace_mask
np->trace_mask = p->trace_mask;
...
}
kernel/syscall.c
的 syscall()
要在 system call return 时,判断要不要 print 出资讯static char *syscall_names[] = {
[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 ((1 << num) & p->trace_mask)
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;
}
}
<<: [Day18] Null byte Injection
>>: Day 20 AWS云端实作起手式最後一弹 整体架构回顾
学习 Angular 的过程中,遇到了一些教学资源不见得会遇到的问题,还真是家常便饭,而且你个人电脑...
前一篇,我们完成了需求一: 当使用者在关键字搜寻这个 input 输入文字时,要在输入框的正下方显示...
读写档案 | 跳脱字元\ | 逐字识别码@ | DateTime | Random ...
本文章同步发布於 我的部落格 什麽是 Rack ? Rack 是 Ruby 所有的网路框架背後最底层...
在网页的互动效果中,常常是当使用者符合了某个条件时,画面上的元素产生令人惊喜的变化,这些变化可简单可...