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:
Robert Morris 2019-08-05 02:04:44 -04:00
parent deec67f05d
commit d96a8c5661
2 changed files with 118 additions and 84 deletions

View file

@ -117,72 +117,13 @@ time in xv6, determined by how often a hardware timer generates
interrupts.
<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>
#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();
}
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
<p>
<tt>alarmtest</tt> calls <tt>sigalarm(2, periodic)</tt> in <tt>test0</tt> to
ask the kernel to force a call to <tt>periodic()</tt> every 2 ticks,
and then spins for a while.
You can see the assembly
@ -194,24 +135,23 @@ When you've finished the lab,
<pre>
$ alarmtest
test0 start
...................................................alarm!
.............................................................alarm!
(repeated many times)
test0 done
......................................alarm!
test0 passed
test1 start
..alarm!
..alarm!
..alarm!
(repeated many times)
test1 done
.alarm!
..alarm!
..alarm!
..alarm!
..alarm!
..alarm!
..alarm!
test1 passed
$
</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
when the process's alarm interval expires. You'll need to modify
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
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>
@ -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>
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>
int sigalarm(int ticks, void (*handler)());
int sigreturn(void);
</pre>
<li>Update user/sys.pl (which generates user/usys.S),
kernel/syscall.h, and kernel/syscall.c
to allow <tt>alarmtest</tt> to invoke the sigalarm system
call.
to allow <tt>alarmtest</tt> to invoke the sigalarm and
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
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
registers---what registers do you need to save and restore to resume
the interrupted code correctly? (Hint: it will be many).
Several approaches are possible; one convenient plan is to add another
system call <tt>sigreturn</tt> that the user-space alarm handler calls when it is
done, and which restores registers and returns to the original
Several approaches are possible; for this lab you should make
the <tt>sigreturn</tt> system call
restore registers and return to the original
interrupted user instruction.
The user-space alarm handler
calls sigreturn when it is done.
Some hints:
<ul>
<li>Add a new <tt>sigreturn</tt> system call.
<li>Have <tt>usertrap</tt> save enough state in
<tt>struct proc</tt> when the timer goes off
that <tt>sigreturn</tt> can correctly return to the

88
user/alarmtest.c Normal file
View 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");
}
}