add alarmtest.c to the repository
they don't have to modify alarmtest.c, so we can use the original version to test, to make it harder to cheat.
This commit is contained in:
parent
deec67f05d
commit
d96a8c5661
|
@ -117,72 +117,13 @@ time in xv6, determined by how often a hardware timer generates
|
||||||
interrupts.
|
interrupts.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You should put the following test program in <tt>user/alarmtest.c</tt>:
|
You'll find a file <tt>user/alarmtest.c</tt> in your xv6
|
||||||
|
repository. Add it to the Makefile. It won't compile correctly
|
||||||
|
until you've added <tt>sigalarm</tt> and <tt>sigreturn</tt>
|
||||||
|
system calls (see below).
|
||||||
|
|
||||||
<pre>
|
<p>
|
||||||
#include "kernel/param.h"
|
<tt>alarmtest</tt> calls <tt>sigalarm(2, periodic)</tt> in <tt>test0</tt> to
|
||||||
#include "kernel/types.h"
|
|
||||||
#include "kernel/stat.h"
|
|
||||||
#include "kernel/riscv.h"
|
|
||||||
#include "user/user.h"
|
|
||||||
|
|
||||||
void test0();
|
|
||||||
void test1();
|
|
||||||
void periodic();
|
|
||||||
|
|
||||||
int
|
|
||||||
main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
test0();
|
|
||||||
test1();
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void test0()
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
printf(1, "test0 start\n");
|
|
||||||
sigalarm(2, periodic);
|
|
||||||
for(i = 0; i < 1000*500000; i++){
|
|
||||||
if((i % 250000) == 0)
|
|
||||||
write(2, ".", 1);
|
|
||||||
}
|
|
||||||
sigalarm(0, 0);
|
|
||||||
printf(1, "test0 done\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
periodic()
|
|
||||||
{
|
|
||||||
printf(1, "alarm!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void __attribute__ ((noinline)) foo(int i, int *j) {
|
|
||||||
if((i % 2500000) == 0) {
|
|
||||||
write(2, ".", 1);
|
|
||||||
}
|
|
||||||
*j += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void test1() {
|
|
||||||
int i;
|
|
||||||
int j;
|
|
||||||
|
|
||||||
printf(1, "test1 start\n");
|
|
||||||
j = 0;
|
|
||||||
sigalarm(2, periodic);
|
|
||||||
for(i = 0; i < 1000*500000; i++){
|
|
||||||
foo(i, &j);
|
|
||||||
}
|
|
||||||
if(i != j) {
|
|
||||||
printf(2, "i %d should = j %d\n", i, j);
|
|
||||||
exit();
|
|
||||||
}
|
|
||||||
printf(1, "test1 done\n");
|
|
||||||
}
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
The program calls <tt>sigalarm(2, periodic1)</tt> in <tt>test0</tt> to
|
|
||||||
ask the kernel to force a call to <tt>periodic()</tt> every 2 ticks,
|
ask the kernel to force a call to <tt>periodic()</tt> every 2 ticks,
|
||||||
and then spins for a while.
|
and then spins for a while.
|
||||||
You can see the assembly
|
You can see the assembly
|
||||||
|
@ -194,24 +135,23 @@ When you've finished the lab,
|
||||||
<pre>
|
<pre>
|
||||||
$ alarmtest
|
$ alarmtest
|
||||||
test0 start
|
test0 start
|
||||||
...................................................alarm!
|
......................................alarm!
|
||||||
.............................................................alarm!
|
test0 passed
|
||||||
(repeated many times)
|
|
||||||
test0 done
|
|
||||||
test1 start
|
test1 start
|
||||||
..alarm!
|
..alarm!
|
||||||
..alarm!
|
..alarm!
|
||||||
..alarm!
|
..alarm!
|
||||||
(repeated many times)
|
.alarm!
|
||||||
test1 done
|
..alarm!
|
||||||
|
..alarm!
|
||||||
|
..alarm!
|
||||||
|
..alarm!
|
||||||
|
..alarm!
|
||||||
|
..alarm!
|
||||||
|
test1 passed
|
||||||
$
|
$
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>
|
|
||||||
At first, however, you'll see that alarmtest only prints periods,
|
|
||||||
and doesn't print "alarm!".
|
|
||||||
|
|
||||||
|
|
||||||
<p>The main challenge will be to arrange that the handler is invoked
|
<p>The main challenge will be to arrange that the handler is invoked
|
||||||
when the process's alarm interval expires. You'll need to modify
|
when the process's alarm interval expires. You'll need to modify
|
||||||
usertrap() in kernel/trap.c so that when a
|
usertrap() in kernel/trap.c so that when a
|
||||||
|
@ -223,6 +163,9 @@ and doesn't print "alarm!".
|
||||||
|
|
||||||
<p>Your solution will be only a few lines of code, but it may be tricky to
|
<p>Your solution will be only a few lines of code, but it may be tricky to
|
||||||
get it right.
|
get it right.
|
||||||
|
We'll test your code with the version of alarmtest.c in the original
|
||||||
|
repository; if you modify alarmtest.c, make sure your kernel changes
|
||||||
|
cause the original alarmtest to pass the tests.
|
||||||
|
|
||||||
<h3>test0: invoke handler</h3>
|
<h3>test0: invoke handler</h3>
|
||||||
|
|
||||||
|
@ -236,15 +179,18 @@ program crashes after printing "alarm!". Here are some hints:
|
||||||
<li>You'll need to modify the Makefile to cause <tt>alarmtest.c</tt>
|
<li>You'll need to modify the Makefile to cause <tt>alarmtest.c</tt>
|
||||||
to be compiled as an xv6 user program.
|
to be compiled as an xv6 user program.
|
||||||
|
|
||||||
<li>The right declaration to put in <tt>user/user.h</tt> is:
|
<li>The right declarations to put in <tt>user/user.h</tt> are:
|
||||||
<pre>
|
<pre>
|
||||||
int sigalarm(int ticks, void (*handler)());
|
int sigalarm(int ticks, void (*handler)());
|
||||||
|
int sigreturn(void);
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<li>Update user/sys.pl (which generates user/usys.S),
|
<li>Update user/sys.pl (which generates user/usys.S),
|
||||||
kernel/syscall.h, and kernel/syscall.c
|
kernel/syscall.h, and kernel/syscall.c
|
||||||
to allow <tt>alarmtest</tt> to invoke the sigalarm system
|
to allow <tt>alarmtest</tt> to invoke the sigalarm and
|
||||||
call.
|
sigreturn system calls.
|
||||||
|
|
||||||
|
<li>For now, your <tt>sys_sigreturn</tt> should just return zero.
|
||||||
|
|
||||||
<li>Your <tt>sys_sigalarm()</tt> should store the alarm interval and
|
<li>Your <tt>sys_sigalarm()</tt> should store the alarm interval and
|
||||||
the pointer to the handler function in new fields in the <tt>proc</tt>
|
the pointer to the handler function in new fields in the <tt>proc</tt>
|
||||||
|
@ -300,15 +246,15 @@ can continue undisturbed after the alarm.
|
||||||
<p>Your solution is likely to require you to save and restore
|
<p>Your solution is likely to require you to save and restore
|
||||||
registers---what registers do you need to save and restore to resume
|
registers---what registers do you need to save and restore to resume
|
||||||
the interrupted code correctly? (Hint: it will be many).
|
the interrupted code correctly? (Hint: it will be many).
|
||||||
Several approaches are possible; one convenient plan is to add another
|
Several approaches are possible; for this lab you should make
|
||||||
system call <tt>sigreturn</tt> that the user-space alarm handler calls when it is
|
the <tt>sigreturn</tt> system call
|
||||||
done, and which restores registers and returns to the original
|
restore registers and return to the original
|
||||||
interrupted user instruction.
|
interrupted user instruction.
|
||||||
|
The user-space alarm handler
|
||||||
|
calls sigreturn when it is done.
|
||||||
|
|
||||||
Some hints:
|
Some hints:
|
||||||
<ul>
|
<ul>
|
||||||
<li>Add a new <tt>sigreturn</tt> system call.
|
|
||||||
|
|
||||||
<li>Have <tt>usertrap</tt> save enough state in
|
<li>Have <tt>usertrap</tt> save enough state in
|
||||||
<tt>struct proc</tt> when the timer goes off
|
<tt>struct proc</tt> when the timer goes off
|
||||||
that <tt>sigreturn</tt> can correctly return to the
|
that <tt>sigreturn</tt> can correctly return to the
|
||||||
|
|
88
user/alarmtest.c
Normal file
88
user/alarmtest.c
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
//
|
||||||
|
// test program for the alarm lab.
|
||||||
|
// you can modify this file for testing,
|
||||||
|
// but please make sure your kernel
|
||||||
|
// modifications pass the original
|
||||||
|
// versions of these tests.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "kernel/param.h"
|
||||||
|
#include "kernel/types.h"
|
||||||
|
#include "kernel/stat.h"
|
||||||
|
#include "kernel/riscv.h"
|
||||||
|
#include "user/user.h"
|
||||||
|
|
||||||
|
void test0();
|
||||||
|
void test1();
|
||||||
|
void periodic();
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
test0();
|
||||||
|
test1();
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
volatile static int count;
|
||||||
|
|
||||||
|
void
|
||||||
|
periodic()
|
||||||
|
{
|
||||||
|
count = count + 1;
|
||||||
|
printf(1, "alarm!\n");
|
||||||
|
sigreturn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests whether the kernel calls
|
||||||
|
// the alarm handler even a single time.
|
||||||
|
void
|
||||||
|
test0()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
printf(1, "test0 start\n");
|
||||||
|
count = 0;
|
||||||
|
sigalarm(2, periodic);
|
||||||
|
for(i = 0; i < 1000*500000; i++){
|
||||||
|
if((i % 250000) == 0)
|
||||||
|
write(2, ".", 1);
|
||||||
|
if(count > 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sigalarm(0, 0);
|
||||||
|
if(count > 0){
|
||||||
|
printf(1, "test0 passed\n");
|
||||||
|
} else {
|
||||||
|
printf(1, "test0 failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void __attribute__ ((noinline)) foo(int i, int *j) {
|
||||||
|
if((i % 2500000) == 0) {
|
||||||
|
write(2, ".", 1);
|
||||||
|
}
|
||||||
|
*j += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
test1()
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int j;
|
||||||
|
|
||||||
|
printf(1, "test1 start\n");
|
||||||
|
count = 0;
|
||||||
|
j = 0;
|
||||||
|
sigalarm(2, periodic);
|
||||||
|
for(i = 0; i < 500000000; i++){
|
||||||
|
if(count >= 10)
|
||||||
|
break;
|
||||||
|
foo(i, &j);
|
||||||
|
}
|
||||||
|
if(i != j || count < 10){
|
||||||
|
// i should equal j
|
||||||
|
printf(1, "test1 failed\n");
|
||||||
|
} else {
|
||||||
|
printf(1, "test1 passed\n");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue