Skip to content

Alarm(Hard)

添加一个新的sigalarm(interval, handler)系统调用

test0: invoke handler(调用处理程序)

添加系统调用流程(类似Lab2)

修改Makefile以使alarmtest.c被编译为xv6用户程序

diff --git a/Makefile b/Makefile
index 1fa367e..f5da769 100644
--- a/Makefile
+++ b/Makefile
@@ -175,7 +175,7 @@ UPROGS=\
        $U/_grind\
        $U/_wc\
        $U/_zombie\

       $U/_alarmtest\

放入user/user.h的正确声明

diff --git a/user/user.h b/user/user.h
index b71ecda..57404e0 100644
--- a/user/user.h
+++ b/user/user.h
@@ -23,6 +23,8 @@ int getpid(void);
 char* sbrk(int);
 int sleep(int);
 int uptime(void);
int sigalarm(int ticks, void (*handler)());
int sigreturn(void);
 
 // ulib.c
 int stat(const char*, struct stat*);

更新user/usys.pl

此文件生成user/usys.S

diff --git a/user/usys.pl b/user/usys.pl
index 01e426e..fa548b0 100755
--- a/user/usys.pl
+++ b/user/usys.pl
@@ -36,3 +36,5 @@ entry("getpid");
 entry("sbrk");
 entry("sleep");
 entry("uptime");
entry("sigalarm");
entry("sigreturn");

更新kernel/syscall.h和kernel/syscall.c

以此允许alarmtest调用sigalarm和sigreturn系统调用

diff --git a/kernel/syscall.h b/kernel/syscall.h
index bc5f356..7b88b81 100644
--- a/kernel/syscall.h
+++ b/kernel/syscall.h
@@ -20,3 +20,5 @@
 #define SYS_link   19
 #define SYS_mkdir  20
 #define SYS_close  21
#define SYS_sigalarm  22
#define SYS_sigreturn  23
diff --git a/kernel/syscall.c b/kernel/syscall.c
index c1b3670..8c0a928 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -104,6 +104,8 @@ extern uint64 sys_unlink(void);
 extern uint64 sys_wait(void);
 extern uint64 sys_write(void);
 extern uint64 sys_uptime(void);
extern uint64 sys_sigalarm(void);
extern uint64 sys_sigreturn(void);
 
 static uint64 (*syscalls[])(void) = {
 [SYS_fork]    sys_fork,
@@ -127,6 +129,8 @@ static uint64 (*syscalls[])(void) = {
 [SYS_link]    sys_link,
 [SYS_mkdir]   sys_mkdir,
 [SYS_close]   sys_close,
[SYS_sigalarm]   sys_sigalarm,
[SYS_sigreturn]  sys_sigreturn,
 };
 
 void

sys_sigreturn和sys_sigalarm系统调用

sys_sigreturn系统调用返回应该是零

sys_sigalarm()应该将报警间隔和指向处理程序函数的指针存储在struct proc的新字段中

diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index fd19d2a..f3c86d8 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -96,3 +96,26 @@ sys_uptime(void)
   release(&tickslock);
   return xticks;
 }

uint64
sys_sigalarm(void)
{
  int interval;
  uint64 handler;

  argint(0, &interval);
  argaddr(1, &handler);

  struct proc *p = myproc();
  p->interval = interval;
  p->handler = handler;
  p->ticks = 0;

  return 0;
}

uint64
sys_sigreturn(void)
{
  return 0;
}
\ No newline at end of file

proc字段

在struct proc新增一个新字段。用于跟踪自上一次调用(或直到下一次调用)到进程的报警处理程序间经历了多少滴答

diff --git a/kernel/proc.h b/kernel/proc.h
index 9c16ea7..3e5cd13 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -103,4 +103,8 @@ struct proc {
   struct file *ofile[NOFILE];  // Open files
   struct inode *cwd;           // Current directory
   char name[16];               // Process name (debugging)

  int interval;
  uint64 handler;
  int ticks;
 };

在proc.c的allocproc()中初始化proc字段

diff --git a/kernel/proc.c b/kernel/proc.c
index dab1e1d..cae7085 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -126,6 +126,9 @@ found:
   memset(&p->context, 0, sizeof(p->context));
   p->context.ra = (uint64)forkret;
   p->context.sp = p->kstack + PGSIZE;
  p->interval = 0;
  p->handler = 0;
  p->ticks = 0;
 
   return p;
 }

中断在kernel/trap.c中的usertrap()中处理

每一个滴答声,硬件时钟就会强制一个中断

diff --git a/kernel/trap.c b/kernel/trap.c
index a63249e..8230374 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -78,7 +78,16 @@ usertrap(void)
 
   // give up the CPU if this is a timer interrupt.
   if(which_dev == 2)
  {
    if(p->interval != 0){
      p->ticks++;
      if(p->ticks > p->interval){
        p->ticks = 0;
        p->trapframe->epc = p->handler;
      }
    }
     yield();
  }
 
   usertrapret();
 }

什么决定了用户空间代码恢复执行的指令地址?

在proc.h第48行

  /*  24 */ uint64 epc;           // saved user program counter

用户空间的pc

结果

bash
$ alarmtest
test0 start
................................................alarm!
test0 passed

test1/test2(): resume interrupted code(恢复被中断的代码)

修改 proc 结构体

增加 trapframe 的副本字段

diff --git a/kernel/proc.h b/kernel/proc.h
index 3e5cd13..c8aa74d 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -107,4 +107,5 @@ struct proc {
   int interval;
   uint64 handler;
   int ticks;
  struct trapframe* trapframecopy;
 };

与test0相同将其在proc中进行初始化

diff --git a/kernel/proc.c b/kernel/proc.c
index cae7085..704c016 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -129,6 +129,7 @@ found:
   p->interval = 0;
   p->handler = 0;
   p->ticks = 0;
  p->trapframecopy = 0;
 
   return p;
 }

保留现场

在 kernel/trap.c 的 usertrap() 中覆盖 p->trapframe->epc 前做 trapframe 的副本

diff --git a/kernel/trap.c b/kernel/trap.c
index 8230374..6d0ba1c 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -81,8 +81,9 @@ usertrap(void)
   {
     if(p->interval != 0){
       p->ticks++;
      if(p->ticks > p->interval){
        p->ticks = 0;
      if(p->ticks == p->interval){
        p->trapframecopy = p->trapframe + 512;
        memmove(p->trapframecopy,p->trapframe,sizeof(struct trapframe));
         p->trapframe->epc = p->handler;
       }
     }

使用memmove函数进行复制整个trapframe

512是使用trapframe空间后面的内存, trapframe实际内存大小为288B, 可以取其他值

恢复现场

在 sys_sigreturn() 中将副本恢复到原 trapframe

diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index f3c86d8..242a0d1 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -117,5 +117,13 @@ sys_sigalarm(void)
 uint64
 sys_sigreturn(void)
 {
  return 0;
  struct proc *p = myproc();
  if(p->trapframecopy != p->trapframe + 512) {
    return -1;
  }
  memmove(p->trapframe, p->trapframecopy, sizeof(struct trapframe));

  p->ticks = 0;
  p->trapframecopy = 0;
  return p->trapframe->a0;
 }
\ No newline at end of file

防止对handler程序的重复调用

  1. 将p->ticks=0从原本的 usertrap() 移至 sys_sigreturn() 中

若在usetrap()中进行重置,ticks后续会自增又会满足handler调用条件

而sys_sigreturn()的结束对应handler()的结束,所以移动到此处

  1. 注意当ticks达到 interval 时调用handler()

结果

测试test0、test1和test2

bash
$ alarmtest
test0 start
.............................alarm!
test0 passed
test1 start
....alarm!
...alarm!
....alarm!
....alarm!
....alarm!
.....alarm!
....alarm!
.....alarm!
....alarm!
.....alarm!
test1 passed
test2 start
......................................................alarm!
test2 passed

usertests确保没有破坏内核的任何其他部分

bash
ALL TESTS PASSED
$