实验要求:详见这里
要点:
- 实现系统调用
sigalarm(n, fn),让函数fn每n个tick被调用一次。这里需要注意的是,传入的函数fn的地址是用户态函数的虚拟地址。时钟中断发生后,内核如何让用户态的代码执行流切到函数fn执行,这个可以通过设置sepc寄存器,返回用户态时,返回到函数fn执行,而不是返回原来的用户态进程被打断的地方继续执行。 fn函数执行完成后,要返回原来的代码执行流继续执行。在函数fn的最后,调用另一个系统调用sigreturn,返回用户空间原来的执行流。这就需要内核在返回fn函数执行之前,保存用户态的现场。sigreturn时恢复。fn函数在没有执行完毕之前,应该禁止再次调用。也就是内核需要知道当进程的用户态代码是否是处在函数fn的执行流里。- 要注意到,
sigreturn是一个系统调用,它也有返回值,它的返回值也会破坏寄存器a0中的原有值。
关键的修改记录:
diff --git a/kernel/proc.h b/kernel/proc.h
index d021857..cd5ba44 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -104,4 +104,11 @@ struct proc {
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
+
+ // sigalarm
+ int siginterval;
+ int sigtickcnt;
+ int sigrunning;
+ uint64 sighandler;
+ struct trapframe sigtrapframe;
};
diff --git a/kernel/proc.c b/kernel/proc.c
index 58a8a0b..b386e04 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -140,6 +140,12 @@ found:
return 0;
}
+ // sig init
+ p->sighandler = 0;
+ p->siginterval = 0;
+ p->sigtickcnt = 0;
+ p->sigrunning = 0;
+
// Set up new context to start executing at forkret,
// which returns to user space.
memset(&p->context, 0, sizeof(p->context));
@@ -168,6 +174,11 @@ freeproc(struct proc *p)
p->chan = 0;
p->killed = 0;
p->xstate = 0;
+ // sig init
+ p->sighandler = 0;
+ p->siginterval = 0;
+ p->sigtickcnt = 0;
+ p->sigrunning = 0;
p->state = UNUSED;
}
@@ -686,3 +697,19 @@ procdump(void)
printf("\n");
}
}
+
+// sig tickcnt++
+void
+proc_sigtickcnt_inc(void)
+{
+ struct proc *p;
+ for(p = proc; p < &proc[NPROC]; p++) {
+ acquire(&p->lock);
+ if (p->siginterval != 0) {
+ p->sigtickcnt++;
+ }
+ release(&p->lock);
+ }
+ return;
+}
diff --git a/kernel/sysproc.c b/kernel/sysproc.c
index 3b4d5bd..00595b3 100644
--- a/kernel/sysproc.c
+++ b/kernel/sysproc.c
@@ -91,3 +91,33 @@ sys_uptime(void)
release(&tickslock);
return xticks;
}
+
+uint64
+sys_sigalarm(void)
+{
+ int sigticks;
+ uint64 handler;
+ struct proc *p;
+
+ argint(0, &sigticks);
+ argaddr(1, &handler);
+
+ if ((sigticks == 0) && (handler != 0)) {
+ return -1;
+ }
+
+ p = myproc();
+ p->siginterval = sigticks;
+ p->sighandler = handler;
+ return 0;
+}
+
+uint64
+sys_sigreturn(void)
+{
+ struct proc *p = myproc();
+ memmove((void *)(p->trapframe), (const void *)&(p->sigtrapframe), sizeof(struct trapframe));
+ p->sigrunning = 0;
+ p->sigtickcnt = 0;
+ return p->trapframe->a0;
+}
diff --git a/kernel/trap.c b/kernel/trap.c
index 512c850..370400e 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -77,8 +77,10 @@ usertrap(void)
exit(-1);
// give up the CPU if this is a timer interrupt.
- if(which_dev == 2)
+ if(which_dev == 2) {
+ proc_sigtickcnt_inc();
yield();
+ }
usertrapret();
}
@@ -96,6 +98,12 @@ usertrapret(void)
// we're back in user space, where usertrap() is correct.
intr_off();
+ if ((p->sigrunning == 0) && (p->siginterval != 0) && (p->sigtickcnt >= p->siginterval)) {
+ p->sigrunning = 1;
+ memmove((void *)&(p->sigtrapframe), (const void *)(p->trapframe), sizeof(struct trapframe));
+ p->trapframe->epc = p->sighandler;
+ }
+
// send syscalls, interrupts, and exceptions to uservec in trampoline.S
uint64 trampoline_uservec = TRAMPOLINE + (uservec - trampoline);
w_stvec(trampoline_uservec);
@@ -150,6 +158,9 @@ kerneltrap()
panic("kerneltrap");
}
+ if(which_dev == 2)
+ proc_sigtickcnt_inc();
+
// give up the CPU if this is a timer interrupt.
if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING)
yield();
代码调试记录:
在实现 sys_sigalarm 函数时,由于自己手欠,将 argaddr(1, &handler); 写成了 argaddr(0, &handler);,导致函数地址错误的获取成了2,本来应该为0的。
如下图所示:
导致返回到 fn 执行时,少执行了第一条指令,于是后面的代码破坏了原有的代码执行流的栈上的内容。导致测试用例 test1 测试不通过,同时也是 test3 测试时异常的原因,因为PC值不对齐。
$ alarmtest
test0 start
..alarm!
test0 passed
test1 start
..alarm!
.alarm!
.alarm!
.alarm!
.alarm!
.alarm!
.alarm!
.alarm!
.alarm!
.alarm!
test1 failed: foo() executed fewer times than it was called, i:1, j:625
test2 start
........................alarm!
alarm!
test2 passed
test3 start
usertrap(): unexpected scause 0x0000000000000002 pid=3
sepc=0x000000000000000d stval=0x0000000000000000
$ QEMU: Terminated
另外,我对 sigalarm 做了一点点的改进。详见这里