xv6 syscall lab

  • 学习syscall lab

  An operating system supports two modes; the kernel mode and the user mode.When a program in user mode requires access to RAM or a hardware resource, it must ask the kernel to provide access to that particular resource. This is done via a system call. When a program makes a system call, the mode is switched from user mode to kernel mode.

  There are many system calls in an operating system which executes different types of tasks when they are called.

实验1:System call tracing
  Your first task is to modify the xv6 kernel to print out a line for each system call invocation. It is enough to print the name of the system call and the return value; you don’t need to print the system call arguments.

 //syscall.c                                                                                                                 
  132 static char syscalls_names[][6] = {
  133 [SYS_fork]    "fork",
  134 [SYS_exit]    "exit",
  135 [SYS_wait]    "wait",
  136 [SYS_pipe]    "pipe",
  137 [SYS_read]    "read",
  138 [SYS_kill]    "kill",
  139 [SYS_exec]    "exec",
  140 [SYS_fstat]   "fstat",
  141 [SYS_chdir]   "chdir",
  142 [SYS_dup]     "dup",
  143 [SYS_getpid]  "getpid",
  144 [SYS_sbrk]    "sbrk",
  145 [SYS_sleep]   "sleep",
  146 [SYS_uptime]  "uptime",
  147 [SYS_open]    "open",
  148 [SYS_write]   "write",
  149 [SYS_mknod]   "mknod",
  150 [SYS_unlink]  "unlink",
  151 [SYS_link]    "link",
  152 [SYS_mkdir]   "mkdir",
  153 [SYS_close]   "close",
  154 };
 
  156 void
  157 syscall(void)
  158 {
  159   ...
  162   num = curproc->tf->eax;
  163   if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
  164     curproc->tf->eax = syscalls[num]();
  165   ++cprintf("\talbert:syscall name:%s\t -> num:%d\n",syscalls_names[num],num);

实验2 :add a Date system call

diff --git a/Makefile b/Makefile
index 09d790c..25420f1 100644
--- a/Makefile
+++ b/Makefile
@@ -181,6 +181,7 @@ UPROGS=\
 	_usertests\
 	_wc\
 	_zombie\
+	_date\
 
 fs.img: mkfs README $(UPROGS)
 	./mkfs fs.img README $(UPROGS)
@@ -249,7 +250,7 @@ qemu-nox-gdb: fs.img xv6.img .gdbinit
 
 EXTRA=\
 	mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c\
-	ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c\
+	ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c date.c\
 	printf.c umalloc.c\
 	README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\
 	.gdbinit.tmpl gdbutil\
diff --git a/syscall.c b/syscall.c
index ee85261..9ad01e6 100644
--- a/syscall.c
+++ b/syscall.c
@@ -104,6 +104,9 @@ extern int sys_wait(void);
 extern int sys_write(void);
 extern int sys_uptime(void);
 
+//albert
+extern int sys_date(void);
+
 static int (*syscalls[])(void) = {
 [SYS_fork]    sys_fork,
 [SYS_exit]    sys_exit,
@@ -126,8 +129,36 @@ static int (*syscalls[])(void) = {
 [SYS_link]    sys_link,
 [SYS_mkdir]   sys_mkdir,
 [SYS_close]   sys_close,
+//albert
+[SYS_date]    sys_date,
 };
 
+//albert
+/*static char syscalls_names[][6] = {
+[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_date]    "date",
+};*/
+
diff --git a/syscall.h b/syscall.h
index bc5f356..61a5b46 100644
--- a/syscall.h
+++ b/syscall.h
@@ -20,3 +20,5 @@
 #define SYS_link   19
 #define SYS_mkdir  20
 #define SYS_close  21
+//albert
+#define SYS_date  22
diff --git a/sysproc.c b/sysproc.c
index 0686d29..6491ddd 100644
--- a/sysproc.c
+++ b/sysproc.c
@@ -89,3 +89,15 @@ sys_uptime(void)
   release(&tickslock);
   return xticks;
 }
+
+
+
+//albert:return current date 
+int 
+sys_date(struct rtcdate *r)
+{
+	if(argptr(0,(void *)&r,sizeof(*r)) < 0)
+		return -1;
+	cmostime(r);
+	return 0;
+}

diff --git a/user.h b/user.h
index 4f99c52..d5aea54 100644
--- a/user.h
+++ b/user.h
@@ -23,6 +23,8 @@ int getpid(void);
 char* sbrk(int);
 int sleep(int);
 int uptime(void);
+//albert
+int date(struct rtcdate *);
 
 // ulib.c
 int stat(const char*, struct stat*);
diff --git a/usys.S b/usys.S
index 8bfd8a1..d83555f 100644
--- a/usys.S
+++ b/usys.S
@@ -29,3 +29,5 @@ SYSCALL(getpid)
 SYSCALL(sbrk)
 SYSCALL(sleep)
 SYSCALL(uptime)
+//albert
+SYSCALL(date)

添加patch之后,执行make qemu-nox,然后ls,显示添加的系统调用date。

$ ls
.              1 1 512
..             1 1 512
README         2 2 2327
cat            2 3 14509
echo           2 4 13366
forktest       2 5 8196
grep           2 6 16045
init           2 7 14247
kill           2 8 13386
ln             2 9 13332
ls             2 10 16196
mkdir          2 11 13427
rm             2 12 13404
sh             2 13 24848
stressfs       2 14 14342
usertests      2 15 67274
wc             2 16 15167
zombie         2 17 13056
date           2 18 13609
console        3 19 0

执行date:

$ date
4-6 2019 5:20:45
pid 4 date: trap 14 err 5 on cpu 1 eip 0xffffffff addr 0xffffffff--kill proc

通过如下命令查看date系统调用:

zhaoxiao@zhaoxiao:~/Documents/work/code/xv6/xv6-public$ gcc -E -P usys.S -I ./include/
.globl fork; fork: movl $1, %eax; int $64; ret
.globl exit; exit: movl $2, %eax; int $64; ret
.globl wait; wait: movl $3, %eax; int $64; ret
.globl pipe; pipe: movl $4, %eax; int $64; ret
.globl read; read: movl $5, %eax; int $64; ret
.globl write; write: movl $16, %eax; int $64; ret
.globl close; close: movl $21, %eax; int $64; ret
.globl kill; kill: movl $6, %eax; int $64; ret
.globl exec; exec: movl $7, %eax; int $64; ret
.globl open; open: movl $15, %eax; int $64; ret
.globl mknod; mknod: movl $17, %eax; int $64; ret
.globl unlink; unlink: movl $18, %eax; int $64; ret
.globl fstat; fstat: movl $8, %eax; int $64; ret
.globl link; link: movl $19, %eax; int $64; ret
.globl mkdir; mkdir: movl $20, %eax; int $64; ret
.globl chdir; chdir: movl $9, %eax; int $64; ret
.globl dup; dup: movl $10, %eax; int $64; ret
.globl getpid; getpid: movl $11, %eax; int $64; ret
.globl sbrk; sbrk: movl $12, %eax; int $64; ret
.globl sleep; sleep: movl $13, %eax; int $64; ret
.globl uptime; uptime: movl $14, %eax; int $64; ret
.globl date; date: movl $22, %eax; int $64; ret

实验3 – 调试syscall调用流程
系统调用流程为:
  date ->alltraps(trapasm.S)-> trap(trap.c)->syscall(syscall.c )

第一种调试方法:

Documents/work/code/xv6/xv6-public$ make qemu-nox-gdb 
Documents/work/code/xv6/xv6-public$ gdb kernel
(gdb) file date.o
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Load new symbol table from "date.o"? (y or n) y
Reading symbols from date.o...done.
(gdb) b main
Breakpoint 1 at 0x20: file date.c, line 7.
(gdb) c
Continuing.
[New Thread 2]
[Switching to Thread 2]
[  1b:  20]    0x1d0:	rep stos %al,%es:(%di)

Breakpoint 1, main (argc=2, argv=0x0) at date.c:7
7	{

执行si命令直到int $0x40,64 即0x40 是system call in xv6。

│0x3aa   mov    $0xa,%ax                                                                                                    │
│0x3ad   add    %al,(%bx,%si)                                                                                               │
│0x3af   int    $0x40   

当执行了0x3af int $0x40 之后,跳转到如下地址,接着依次跳转到alltraps和trap。

   │0x80105ae8      push   $0x40                                                                                               │
   │0x80105aea      jmp    0x8010540d           //jump   alltraps                                                                           │
   │0x80105aed      (bad)                                                                                                      │
   │0x80105aee      ljmp   *0x0(%bp,%si)                                                                                       │
   │0x80105af1      push   $0x41                                                                                               │
   │0x80105af3      jmp    0x8010540d                                                                                          │
   │0x80105af6      (bad)                                                                                                      │
   │0x80105af7      ljmp   *0x0(%bp,%si)                                                                                       │
   │0x80105afa      push   $0x42 
   ...
    
   |0x8010541f      call   0x80105510       //trap

  syscall通过trapframe中的eax寄存器来确定系统调用号,从而决定调用哪个系统函数,当然eax的值或许是库函数在调用int指令的时候设置的,只是保护现场使得存放在了trapframe中,然后通过系统调用号调用具体的系统调用处理函数并返回到trapframe中的eax位置,这样恢复现场时库函数便能根据eax得到系统调用的返回值。
  每个系统调用都有不同的参数,那么在内核中的系统调用函数是如何找到这些参数?xv6提供了函数 argint、argptr 和 argstr来获得第 n 个系统调用参数。

//syscall.c:
num = curproc->tf->eax;

第二种调试方法:
  首先打开两个窗口,分别执行如下命令:

窗口1:
Documents/work/code/xv6/xv6-public$ make qemu-nox-gdb 
*** Now run 'gdb'.
qemu-system-i386 -nographic -drive file=fs.img,index=1,media=disk,format=raw -drive file=xv6.img,index=0,media=disk,format=raw -smp 2 -m 512  -S -gdb tcp::26000

窗口2:
Documents/work/code/xv6/xv6-public$ gdb
(gdb) b sys_date
Breakpoint 1 at 0x801053d0: file sysproc.c, line 98.
(gdb) c

然后执行:

窗口1:
init:starting sh
$ date

窗口2:
Documents/work/code/xv6/xv6-public$ gdb
(gdb) bt 
#0  sys_date (r=<error reading variable: can't compute CFA for this frame>) at sysproc.c:98
#1  0x8010469a in syscall () at syscall.c:170
#2  0x801056e9 in trap (tf=<error reading variable: can't compute CFA for this frame>) at trap.c:43
#3  0x80105424 in alltraps () at trapasm.S:20
#4  0x8dfbefb4 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

实验3 – xv6 CPU alarm system call

  In this exercise you’ll add a feature to xv6 that periodically alerts a process as it uses CPU time. This might be useful for compute-bound processes that want to limit how much CPU time they chew up, or for processes that want to compute but also want to take some periodic action. More generally, you’ll be implementing a primitive form of user-level interrupt/fault handlers; you could use something similar to handle page faults in the application, for example.

  You should add a new alarm(interval, handler) system call. If an application calls alarm(n, fn), then after every n “ticks” of CPU time that the program consumes, the kernel will cause application function fn to be called. When fn returns, the application will resume where it left off. A tick is a fairly arbitrary unit of time in xv6, determined by how often a hardware timer generates interrupts.

diff --git a/Makefile b/Makefile
index 31df937..ff74cb8 100644
--- a/Makefile
+++ b/Makefile
@@ -182,6 +182,7 @@ UPROGS=\
 	_app\
 	_zombie\
 	_date\
+	_alarmtest\
 
 fs.img: mkfs README $(UPROGS)
 	./mkfs fs.img README $(UPROGS)
@@ -251,7 +252,7 @@ qemu-nox-gdb: fs.img xv6.img .gdbinit
 EXTRA=\
 	mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c\
 	ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c app.c zombie.c date.c\
-	printf.c scanf.c umalloc.c\
+	printf.c scanf.c umalloc.c alarmtest.c\
 	README dot-bochsrc *.pl toc.* runoff runoff1 runoff.list\
 	.gdbinit.tmpl gdbutil\
 
diff --git a/proc.c b/proc.c
index b2344d6..0231fe8 100644
--- a/proc.c
+++ b/proc.c
@@ -88,6 +88,7 @@ allocproc(void)
 found:
   p->state = EMBRYO;
   p->pid = nextpid++;
+  p->curalarmticks = 0;
 
   release(&ptable.lock);
 
diff --git a/proc.h b/proc.h
index 1647114..ff3f020 100644
--- a/proc.h
+++ b/proc.h
@@ -49,6 +49,11 @@ struct proc {
   struct file *ofile[NOFILE];  // Open files
   struct inode *cwd;           // Current directory
   char name[16];               // Process name (debugging)
+  //albert
+  int alarmticks;
+  int curalarmticks;
+  void (*alarmhandler)();
+
 };
 
 // Process memory is laid out contiguously, low addresses first:
diff --git a/syscall.c b/syscall.c
index 9ad01e6..c802423 100644
--- a/syscall.c
+++ b/syscall.c
@@ -106,6 +106,7 @@ extern int sys_uptime(void);
 
 //albert
 extern int sys_date(void);
+extern int sys_alarm(void);
 
 static int (*syscalls[])(void) = {
 [SYS_fork]    sys_fork,
@@ -131,6 +132,7 @@ static int (*syscalls[])(void) = {
 [SYS_close]   sys_close,
 //albert
 [SYS_date]    sys_date,
+[SYS_alarm]    sys_alarm,
 };
 
 //albert
diff --git a/syscall.h b/syscall.h
index 61a5b46..eb161e3 100644
--- a/syscall.h
+++ b/syscall.h
@@ -22,3 +22,4 @@
 #define SYS_close  21
 //albert
 #define SYS_date  22
+#define SYS_alarm  23
diff --git a/sysproc.c b/sysproc.c
index 6491ddd..bda4fb3 100644
--- a/sysproc.c
+++ b/sysproc.c
@@ -101,3 +101,21 @@ sys_date(struct rtcdate *r)
 	cmostime(r);
 	return 0;
 }
+
+
+//albert
+int
+sys_alarm(void)
+{
+	int ticks;
+	void (*handler)();
+	
+	if(argint(0,&ticks) < 0)
+		return -1;
+	if(argptr(1,(char **)&handler,1) < 0)
+		return -1;
+
+	myproc()->alarmticks = ticks;
+	myproc()->alarmhandler = handler;
+	return 0;
+}
diff --git a/trap.c b/trap.c
index 41c66eb..b5418cd 100644
--- a/trap.c
+++ b/trap.c
@@ -54,6 +54,19 @@ trap(struct trapframe *tf)
       wakeup(&ticks);
       release(&tickslock);
     }
+
+	// when timer interrupt comes from userspace
+	if(myproc() && (tf->cs & 3) == 3){
+		myproc()->curalarmticks++;
+		if(myproc()->alarmticks && 
+				myproc()->curalarmticks >= myproc()->alarmticks){
+			tf->esp -= 4;
+			*((uint *)(tf->esp)) = tf->eip;
+			tf->eip = (uint)myproc()->alarmhandler;
+			myproc()->curalarmticks = 0;
+		}
+	}
+	
     lapiceoi();
     break;
   case T_IRQ0 + IRQ_IDE:
diff --git a/user.h b/user.h
index f08b33a..5a5c9ec 100644
--- a/user.h
+++ b/user.h
@@ -25,6 +25,7 @@ int sleep(int);
 int uptime(void);
 //albert
 int date(struct rtcdate *);
+int alarm(int ticks,void (*handler)());
 
 // ulib.c
 int stat(const char*, struct stat*);
diff --git a/usys.S b/usys.S
index d83555f..ea68dfa 100644
--- a/usys.S
+++ b/usys.S
@@ -31,3 +31,4 @@ SYSCALL(sleep)
 SYSCALL(uptime)
 //albert
 SYSCALL(date)
+SYSCALL(alarm)

  alarmtest.c as shown below:

#include "types.h"
#include "stat.h"
#include "user.h"

void periodic();

int
main(int argc, char *argv[])
{
  int i;
  printf(1, "alarmtest starting\n");
  alarm(10, periodic);
  for(i = 0; i < 25*500000; i++){
    if((i % 250000) == 0)
      write(2, ".", 1);
  }
  exit();
}

void
periodic()
{
  printf(1, "alarm!\n");
}

  The program calls alarm(10, periodic) to ask the kernel to force a call to periodic() every 10 ticks, and then spins for a while. After you have implemented the alarm() system call in the kernel, alarmtest should produce output like this:

$ alarmtest
alarmtest starting
.....alarm!
....alarm!
.....alarm!
......alarm!
.....alarm!
....alarm!
....alarm!
......alarm!
.....alarm!
...alarm!
...$ 

  It will be easier to look at traps with gdb if you tell qemu to use only one CPU, which you can do by running:

make CPUS=1 qemu

参考资料:
W4118: interrupt and system call.pdf
https://pdos.csail.mit.edu/6.828/2018/homework/xv6-alarm.html
https://blog.csdn.net/yuchunyu97/article/details/78298171

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值