Pass over lab text
This commit is contained in:
parent
cc1a303d09
commit
734faa27ac
|
@ -8,10 +8,10 @@
|
||||||
<h1>Lab: system calls</h1>
|
<h1>Lab: system calls</h1>
|
||||||
|
|
||||||
This lab makes you familiar with the implementation of system calls.
|
This lab makes you familiar with the implementation of system calls.
|
||||||
In particular, you will implement a new system call: <tt>alarm</tt>.
|
In particular, you will implement a new system
|
||||||
|
calls: <tt>sigalarm</tt> and <tt>sigreturn</tt>.
|
||||||
|
|
||||||
<b>Note: before this lab, it would be good to have recitation section
|
<b>Note: before this lab, it would be good to have recitation section on gdb and understanding assembly</b>
|
||||||
on gdb</b>
|
|
||||||
|
|
||||||
<h2>Warmup: system call tracing</h2>
|
<h2>Warmup: system call tracing</h2>
|
||||||
|
|
||||||
|
@ -78,10 +78,9 @@ void main(void) {
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<p>Since you will be reading and writing RISC-V assembly code for xv6,
|
<p>Read through call.asm and understand it. The instruction manual
|
||||||
you should read through call.asm and understand it. The instruction
|
for RISC-V is in the doc directory (doc/riscv-spec-v2.2.pdf). Here
|
||||||
manual for RISC-V is in the doc directory (doc/riscv-spec-v2.2.pdf).
|
are some questions that you should answer for yourself:
|
||||||
Here are some questions that you should answer for yourself:
|
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<li>Which registers contain arguments to functions? Which
|
<li>Which registers contain arguments to functions? Which
|
||||||
|
@ -110,8 +109,8 @@ user-level interrupt/fault handlers; you could use something similar
|
||||||
to handle page faults in the application, for example.
|
to handle page faults in the application, for example.
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
You should add a new <tt>alarm(interval, handler)</tt> system call.
|
You should add a new <tt>sigalarm(interval, handler)</tt> system call.
|
||||||
If an application calls <tt>alarm(n, fn)</tt>, then after every
|
If an application calls <tt>sigalarm(n, fn)</tt>, then after every
|
||||||
<tt>n</tt> "ticks" of CPU time that the program consumes, the kernel
|
<tt>n</tt> "ticks" of CPU time that the program consumes, the kernel
|
||||||
will cause application function
|
will cause application function
|
||||||
<tt>fn</tt> to be called. When <tt>fn</tt> returns, the application
|
<tt>fn</tt> to be called. When <tt>fn</tt> returns, the application
|
||||||
|
@ -186,12 +185,13 @@ void test1() {
|
||||||
}
|
}
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
The program calls <tt>alarm(2, periodic1)</tt> in test0 to ask the kernel to
|
The program calls <tt>sigalarm(2, periodic1)</tt> in <tt>test0</tt> to
|
||||||
force a call to <tt>periodic()</tt> every 10 ticks, and then spins for
|
ask the kernel to force a call to <tt>periodic()</tt> every 2 ticks,
|
||||||
a while.
|
and then spins for a while. After you have implemented
|
||||||
After you have implemented the <tt>alarm()</tt> system call in the kernel,
|
the <tt>sigalarm()</tt> system call in the kernel,
|
||||||
<tt>alarmtest</tt> should produce output like this for test0:
|
<tt>alarmtest</tt> should produce output like this for <tt>test0</tt>:
|
||||||
|
|
||||||
|
<b>Update output for final usertests.c</b>
|
||||||
<pre>
|
<pre>
|
||||||
$ alarmtest
|
$ alarmtest
|
||||||
alarmtest starting
|
alarmtest starting
|
||||||
|
@ -227,7 +227,7 @@ alarmtest starting
|
||||||
code for the alarmtest program in alarmtest.asm, which will be handy
|
code for the alarmtest program in alarmtest.asm, which will be handy
|
||||||
for debugging.
|
for debugging.
|
||||||
|
|
||||||
<h2>Test0</h2>
|
<h2>Test0: invoke handler</h2>
|
||||||
|
|
||||||
<p>To get started, the best strategy is to first pass test0, which
|
<p>To get started, the best strategy is to first pass test0, which
|
||||||
will force you to handle the main challenge above. Here are some
|
will force you to handle the main challenge above. Here are some
|
||||||
|
@ -240,52 +240,48 @@ to be compiled as an xv6 user program.
|
||||||
|
|
||||||
<li>The right declaration to put in <tt>user/user.h</tt> is:
|
<li>The right declaration to put in <tt>user/user.h</tt> is:
|
||||||
<pre>
|
<pre>
|
||||||
int alarm(int ticks, void (*handler)());
|
int sigalarm(int ticks, void (*handler)());
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<li>Update <tt>kernel/syscall.h</tt> and <tt>user/usys.S</tt> to
|
<li>Update kernel/syscall.h and user/usys.S (update usys.pl to update
|
||||||
allow <tt>alarmtest</tt> to invoke the alarm system call.
|
usys.S) to allow <tt>alarmtest</tt> to invoke the sigalarm system
|
||||||
|
call.
|
||||||
|
|
||||||
<li>Your <tt>sys_alarm()</tt> should store the alarm interval and the
|
<li>Your <tt>sys_sigalarm()</tt> should store the alarm interval and
|
||||||
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>
|
||||||
structure; see <tt>kernel/proc.h</tt>.
|
structure; see <tt>kernel/proc.h</tt>.
|
||||||
|
|
||||||
<li>
|
<li>You'll need to keep track of how many ticks have passed since the
|
||||||
You'll need to keep track of how many ticks have passed since
|
last call (or are left until the next call) to a process's alarm
|
||||||
the last call
|
handler; you'll need a new field in <tt>struct proc</tt> for this
|
||||||
(or are left until the next call) to a process's alarm handler;
|
too. You can initialize <tt>proc</tt> fields in <tt>allocproc()</tt>
|
||||||
you'll need a new field in <tt>struct proc</tt> for this too.
|
|
||||||
You can initialize <tt>proc</tt> fields in <tt>allocproc()</tt>
|
|
||||||
in <tt>proc.c</tt>.
|
in <tt>proc.c</tt>.
|
||||||
|
|
||||||
<li>
|
<li>Every tick, the hardware clock forces an interrupt, which is handled
|
||||||
Every tick, the hardware clock forces an interrupt, which
|
in <tt>usertrap()</tt>; you should add some code here.
|
||||||
is handled in <tt>usertrap()</tt>; you should add some code here.
|
|
||||||
|
|
||||||
<li>
|
<li>You only want to manipulate a process's alarm ticks if there's a a
|
||||||
You only want to manipulate a process's alarm ticks if there's a
|
timer interrupt; you want something like
|
||||||
a timer interrupt; you want something like
|
|
||||||
<pre>
|
<pre>
|
||||||
if(which_dev == 2) ...
|
if(which_dev == 2) ...
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
<li>Don't invoke the process's alarm function, if the processor
|
<li>Only invoke the process's alarm function, if the process has a
|
||||||
doesn't have a timer outstanding. Note that the address of the
|
timer outstanding. Note that the address of the user's alarm
|
||||||
user's alarm function might be 0 (e.g., in
|
function might be 0 (e.g., in alarmtest.asm, <tt>periodic</tt> is at
|
||||||
alarmtest.asm, <tt>period</tt> is at address 0).
|
address 0).
|
||||||
|
|
||||||
<li>
|
<li>It will be easier to look at traps with gdb if you tell qemu to
|
||||||
It will be easier to look at traps with gdb if you tell qemu to use
|
use only one CPU, which you can do by running
|
||||||
only one CPU, which you can do by running
|
|
||||||
<pre>
|
<pre>
|
||||||
make CPUS=1 qemu
|
make CPUS=1 qemu
|
||||||
</pre>
|
</pre>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2>test1()</h2>
|
<h2>test1(): resume interrupted code</h2>
|
||||||
|
|
||||||
<p>Test0 doesn't stress whether the handler returns correctly to
|
<p>Test0 doesn't tests whether the handler returns correctly to
|
||||||
interrupted instruction in test0. If you didn't get this right, it
|
interrupted instruction in test0. If you didn't get this right, it
|
||||||
is likely that test1 will fail (the program crashes or the program
|
is likely that test1 will fail (the program crashes or the program
|
||||||
goes into an infinite loop).
|
goes into an infinite loop).
|
||||||
|
@ -296,15 +292,29 @@ only one CPU, which you can do by running
|
||||||
receives an interrupt, which register contains the address of the
|
receives an interrupt, which register contains the address of the
|
||||||
interrupted instruction?
|
interrupted instruction?
|
||||||
|
|
||||||
<p>Your solution is likely to require you to save and restore a
|
<p>Your solution is likely to require you to save and restore
|
||||||
register. There are several ways to do this. It is ok to change the
|
registers---what registers do you need to save and restore to resume
|
||||||
API of alarm() and have an alarm stub in user space that cooperates
|
the interrupted code correctly? (Hint: it will be many). There are
|
||||||
with the kernel.
|
several ways to do this, but one convenient way is to add another
|
||||||
|
system call <tt>sigreturn</tt> that the handler calls when it is
|
||||||
<p>
|
done. Your job is to arrange that <tt>sigreturn</tt> returns to the
|
||||||
Optional challenges: Prevent re-entrant calls to the handler----if a
|
interrupted code.
|
||||||
handler hasn't returned yet, don't call it again.
|
|
||||||
|
|
||||||
|
Some hints:
|
||||||
|
<ul>
|
||||||
|
<li>Add the <tt>sigreturn</tt> system call, following the changes
|
||||||
|
you made to support <tt>sigalarm</tt>.
|
||||||
|
|
||||||
|
<li>Save enough state when the timer goes in the <tt>struct
|
||||||
|
proc</tt> so that <tt>sigreturn</tt> can return to the
|
||||||
|
interrupted code.
|
||||||
|
|
||||||
|
<li>Prevent re-entrant calls to the handler----if a handler hasn't
|
||||||
|
returned yet, don't call it again.
|
||||||
|
<ul>
|
||||||
|
|
||||||
|
<p>Once you pass <tt>test0</tt> and <tt>test1</tt>, run usertests to
|
||||||
|
make sure you didn't break any other parts of the kernel.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue