Delete a few other no-longer relevant files
This commit is contained in:
parent
6f3a441c10
commit
e627608810
140
TRICKS
140
TRICKS
|
@ -1,140 +0,0 @@
|
|||
This file lists subtle things that might not be commented
|
||||
as well as they should be in the source code and that
|
||||
might be worth pointing out in a longer explanation or in class.
|
||||
|
||||
---
|
||||
|
||||
[2009/07/12: No longer relevant; forkret1 changed
|
||||
and this is now cleaner.]
|
||||
|
||||
forkret1 in trapasm.S is called with a tf argument.
|
||||
In order to use it, forkret1 copies the tf pointer into
|
||||
%esp and then jumps to trapret, which pops the
|
||||
register state out of the trap frame. If an interrupt
|
||||
came in between the mov tf, %esp and the iret that
|
||||
goes back out to user space, the interrupt stack frame
|
||||
would end up scribbling over the tf and whatever memory
|
||||
lay under it.
|
||||
|
||||
Why is this safe? Because forkret1 is only called
|
||||
the first time a process returns to user space, and
|
||||
at that point, cp->tf is set to point to a trap frame
|
||||
constructed at the top of cp's kernel stack. So tf
|
||||
*is* a valid %esp that can hold interrupt state.
|
||||
|
||||
If other tf's were used in forkret1, we could add
|
||||
a cli before the mov tf, %esp.
|
||||
|
||||
---
|
||||
|
||||
In pushcli, must cli() no matter what. It is not safe to do
|
||||
|
||||
if(cpus[cpu()].ncli == 0)
|
||||
cli();
|
||||
cpus[cpu()].ncli++;
|
||||
|
||||
because if interrupts are off then we might call cpu(), get
|
||||
rescheduled to a different cpu, look at cpus[oldcpu].ncli,
|
||||
and wrongly decide not to disable interrupts on the new cpu.
|
||||
|
||||
Instead do
|
||||
|
||||
cli();
|
||||
cpus[cpu()].ncli++;
|
||||
|
||||
always.
|
||||
|
||||
---
|
||||
|
||||
There is a (harmless) race in pushcli, which does
|
||||
|
||||
eflags = readeflags();
|
||||
cli();
|
||||
if(c->ncli++ == 0)
|
||||
c->intena = eflags & FL_IF;
|
||||
|
||||
Consider a bottom-level pushcli.
|
||||
If interrupts are disabled already, then the right thing
|
||||
happens: read_eflags finds that FL_IF is not set,
|
||||
and intena = 0. If interrupts are enabled, then
|
||||
it is less clear that the right thing happens:
|
||||
the readeflags can execute, then the process
|
||||
can get preempted and rescheduled on another cpu,
|
||||
and then once it starts running, perhaps with
|
||||
interrupts disabled (can happen since the scheduler
|
||||
only enables interrupts once per scheduling loop,
|
||||
not every time it schedules a process), it will
|
||||
incorrectly record that interrupts *were* enabled.
|
||||
This doesn't matter, because if it was safe to be
|
||||
running with interrupts enabled before the context
|
||||
switch, it is still safe (and arguably more correct)
|
||||
to run with them enabled after the context switch too.
|
||||
|
||||
In fact it would be safe if scheduler always set
|
||||
c->intena = 1;
|
||||
before calling swtch, and perhaps it should.
|
||||
|
||||
---
|
||||
|
||||
The x86's processor-ordering memory model
|
||||
matches spin locks well, so no explicit memory
|
||||
synchronization instructions are required in
|
||||
acquire and release.
|
||||
|
||||
Consider two sequences of code on different CPUs:
|
||||
|
||||
CPU0
|
||||
A;
|
||||
release(lk);
|
||||
|
||||
and
|
||||
|
||||
CPU1
|
||||
acquire(lk);
|
||||
B;
|
||||
|
||||
We want to make sure that:
|
||||
- all reads in B see the effects of writes in A.
|
||||
- all reads in A do *not* see the effects of writes in B.
|
||||
|
||||
The x86 guarantees that writes in A will go out
|
||||
to memory before the write of lk->locked = 0 in
|
||||
release(lk). It further guarantees that CPU1
|
||||
will observe CPU0's write of lk->locked = 0 only
|
||||
after observing the earlier writes by CPU0.
|
||||
So any reads in B are guaranteed to observe the
|
||||
effects of writes in A.
|
||||
|
||||
According to the Intel manual behavior spec, the
|
||||
second condition requires a serialization instruction
|
||||
in release, to avoid reads in A happening after giving
|
||||
up lk. No Intel SMP processor in existence actually
|
||||
moves reads down after writes, but the language in
|
||||
the spec allows it. There is no telling whether future
|
||||
processors will need it.
|
||||
|
||||
---
|
||||
|
||||
The code in fork needs to read np->pid before
|
||||
setting np->state to RUNNABLE. The following
|
||||
is not a correct way to do this:
|
||||
|
||||
int
|
||||
fork(void)
|
||||
{
|
||||
...
|
||||
np->state = RUNNABLE;
|
||||
return np->pid; // oops
|
||||
}
|
||||
|
||||
After setting np->state to RUNNABLE, some other CPU
|
||||
might run the process, it might exit, and then it might
|
||||
get reused for a different process (with a new pid), all
|
||||
before the return statement. So it's not safe to just
|
||||
"return np->pid". Even saving a copy of np->pid before
|
||||
setting np->state isn't safe, since the compiler is
|
||||
allowed to re-order statements.
|
||||
|
||||
The real code saves a copy of np->pid, then acquires a lock
|
||||
around the write to np->state. The acquire() prevents the
|
||||
compiler from re-ordering.
|
48
cuth
48
cuth
|
@ -1,48 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
$| = 1;
|
||||
|
||||
sub writefile($@){
|
||||
my ($file, @lines) = @_;
|
||||
|
||||
sleep(1);
|
||||
open(F, ">$file") || die "open >$file: $!";
|
||||
print F @lines;
|
||||
close(F);
|
||||
}
|
||||
|
||||
# Cut out #include lines that don't contribute anything.
|
||||
for($i=0; $i<@ARGV; $i++){
|
||||
$file = $ARGV[$i];
|
||||
if(!open(F, $file)){
|
||||
print STDERR "open $file: $!\n";
|
||||
next;
|
||||
}
|
||||
@lines = <F>;
|
||||
close(F);
|
||||
|
||||
$obj = "$file.o";
|
||||
$obj =~ s/\.c\.o$/.o/;
|
||||
system("touch $file");
|
||||
|
||||
if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
|
||||
print STDERR "make $obj failed: $rv\n";
|
||||
next;
|
||||
}
|
||||
|
||||
system("cp $file =$file");
|
||||
for($j=@lines-1; $j>=0; $j--){
|
||||
if($lines[$j] =~ /^#include/){
|
||||
$old = $lines[$j];
|
||||
$lines[$j] = "/* CUT-H */\n";
|
||||
writefile($file, @lines);
|
||||
if(system("make CC='gcc -Werror' $obj >/dev/null 2>\&1") != 0){
|
||||
$lines[$j] = $old;
|
||||
}else{
|
||||
print STDERR "$file $old";
|
||||
}
|
||||
}
|
||||
}
|
||||
writefile($file, grep {!/CUT-H/} @lines);
|
||||
system("rm =$file");
|
||||
}
|
36
pr.pl
36
pr.pl
|
@ -1,36 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
use POSIX qw(strftime);
|
||||
|
||||
if($ARGV[0] eq "-h"){
|
||||
shift @ARGV;
|
||||
$h = $ARGV[0];
|
||||
shift @ARGV;
|
||||
}else{
|
||||
$h = $ARGV[0];
|
||||
}
|
||||
|
||||
$page = 0;
|
||||
$now = strftime "%b %e %H:%M %Y", localtime;
|
||||
|
||||
@lines = <>;
|
||||
for($i=0; $i<@lines; $i+=50){
|
||||
print "\n\n";
|
||||
++$page;
|
||||
print "$now $h Page $page\n";
|
||||
print "\n\n";
|
||||
for($j=$i; $j<@lines && $j<$i +50; $j++){
|
||||
$lines[$j] =~ s!//DOC.*!!;
|
||||
print $lines[$j];
|
||||
}
|
||||
for(; $j<$i+50; $j++){
|
||||
print "\n";
|
||||
}
|
||||
$sheet = "";
|
||||
if($lines[$i] =~ /^([0-9][0-9])[0-9][0-9] /){
|
||||
$sheet = "Sheet $1";
|
||||
}
|
||||
print "\n\n";
|
||||
print "$sheet\n";
|
||||
print "\n\n";
|
||||
}
|
14
printpcs
14
printpcs
|
@ -1,14 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Decode the symbols from a panic EIP list
|
||||
|
||||
# Find a working addr2line
|
||||
for p in i386-jos-elf-addr2line addr2line; do
|
||||
if which $p 2>&1 >/dev/null && \
|
||||
$p -h 2>&1 | grep -q '\belf32-i386\b'; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# Enable as much pretty-printing as this addr2line can do
|
||||
$p $($p -h | grep ' -[aipsf] ' | awk '{print $1}') -e kernel "$@"
|
3
show1
3
show1
|
@ -1,3 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
runoff1 "$@" | pr.pl -h "xv6/$@" | mpage -m50t50b -o -bLetter -T -t -2 -FLucidaSans-Typewriter83 -L60 >x.ps; gv --swap x.ps
|
19
sign.pl
19
sign.pl
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/perl
|
||||
|
||||
open(SIG, $ARGV[0]) || die "open $ARGV[0]: $!";
|
||||
|
||||
$n = sysread(SIG, $buf, 1000);
|
||||
|
||||
if($n > 510){
|
||||
print STDERR "boot block too large: $n bytes (max 510)\n";
|
||||
exit 1;
|
||||
}
|
||||
|
||||
print STDERR "boot block is $n bytes (max 510)\n";
|
||||
|
||||
$buf .= "\0" x (510-$n);
|
||||
$buf .= "\x55\xAA";
|
||||
|
||||
open(SIG, ">$ARGV[0]") || die "open >$ARGV[0]: $!";
|
||||
print SIG $buf;
|
||||
close SIG;
|
134
sleep1.p
134
sleep1.p
|
@ -1,134 +0,0 @@
|
|||
/*
|
||||
This file defines a Promela model for xv6's
|
||||
acquire, release, sleep, and wakeup, along with
|
||||
a model of a simple producer/consumer queue.
|
||||
|
||||
To run:
|
||||
spinp sleep1.p
|
||||
|
||||
(You may need to install Spin, available at http://spinroot.com/.)
|
||||
|
||||
After a successful run spin prints something like:
|
||||
|
||||
unreached in proctype consumer
|
||||
(0 of 37 states)
|
||||
unreached in proctype producer
|
||||
(0 of 23 states)
|
||||
|
||||
After an unsuccessful run, the spinp script prints
|
||||
an execution trace that causes a deadlock.
|
||||
|
||||
The safe body of producer reads:
|
||||
|
||||
acquire(lk);
|
||||
x = value; value = x + 1; x = 0;
|
||||
wakeup(0);
|
||||
release(lk);
|
||||
i = i + 1;
|
||||
|
||||
If this is changed to:
|
||||
|
||||
x = value; value = x + 1; x = 0;
|
||||
acquire(lk);
|
||||
wakeup(0);
|
||||
release(lk);
|
||||
i = i + 1;
|
||||
|
||||
then a deadlock can happen, because the non-atomic
|
||||
increment of value conflicts with the non-atomic
|
||||
decrement in consumer, causing value to have a bad value.
|
||||
Try this.
|
||||
|
||||
If it is changed to:
|
||||
|
||||
acquire(lk);
|
||||
x = value; value = x + 1; x = 0;
|
||||
release(lk);
|
||||
wakeup(0);
|
||||
i = i + 1;
|
||||
|
||||
then nothing bad happens: it is okay to wakeup after release
|
||||
instead of before, although it seems morally wrong.
|
||||
*/
|
||||
|
||||
#define ITER 4
|
||||
#define N 2
|
||||
|
||||
bit lk;
|
||||
byte value;
|
||||
bit sleeping[N];
|
||||
|
||||
inline acquire(x)
|
||||
{
|
||||
atomic { x == 0; x = 1 }
|
||||
}
|
||||
|
||||
inline release(x)
|
||||
{
|
||||
assert x==1;
|
||||
x = 0
|
||||
}
|
||||
|
||||
inline sleep(cond, lk)
|
||||
{
|
||||
assert !sleeping[_pid];
|
||||
if
|
||||
:: cond ->
|
||||
skip
|
||||
:: else ->
|
||||
atomic { release(lk); sleeping[_pid] = 1 };
|
||||
sleeping[_pid] == 0;
|
||||
acquire(lk)
|
||||
fi
|
||||
}
|
||||
|
||||
inline wakeup()
|
||||
{
|
||||
w = 0;
|
||||
do
|
||||
:: w < N ->
|
||||
sleeping[w] = 0;
|
||||
w = w + 1
|
||||
:: else ->
|
||||
break
|
||||
od
|
||||
}
|
||||
|
||||
active[N] proctype consumer()
|
||||
{
|
||||
byte i, x;
|
||||
|
||||
i = 0;
|
||||
do
|
||||
:: i < ITER ->
|
||||
acquire(lk);
|
||||
sleep(value > 0, lk);
|
||||
x = value; value = x - 1; x = 0;
|
||||
release(lk);
|
||||
i = i + 1;
|
||||
:: else ->
|
||||
break
|
||||
od;
|
||||
i = 0;
|
||||
skip
|
||||
}
|
||||
|
||||
active[N] proctype producer()
|
||||
{
|
||||
byte i, x, w;
|
||||
|
||||
i = 0;
|
||||
do
|
||||
:: i < ITER ->
|
||||
acquire(lk);
|
||||
x = value; value = x + 1; x = 0;
|
||||
release(lk);
|
||||
wakeup();
|
||||
i = i + 1;
|
||||
:: else ->
|
||||
break
|
||||
od;
|
||||
i = 0;
|
||||
skip
|
||||
}
|
||||
|
Loading…
Reference in a new issue