replace setjmp/longjmp with swtch

This commit is contained in:
rsc 2007-08-28 12:48:33 +00:00
parent b52dea08bc
commit 818fc0125e
7 changed files with 55 additions and 86 deletions

View file

@ -14,9 +14,9 @@ OBJS = \
picirq.o\ picirq.o\
pipe.o\ pipe.o\
proc.o\ proc.o\
setjmp.o\
spinlock.o\ spinlock.o\
string.o\ string.o\
swtch.o\
syscall.o\ syscall.o\
sysfile.o\ sysfile.o\
sysproc.o\ sysproc.o\
@ -67,7 +67,7 @@ initcode: initcode.S
kernel: $(OBJS) bootother initcode kernel: $(OBJS) bootother initcode
$(LD) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary initcode bootother $(LD) -Ttext 0x100000 -e main -o kernel $(OBJS) -b binary initcode bootother
$(OBJDUMP) -S kernel > kernel.asm $(OBJDUMP) -S kernel > kernel.asm
$(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* //' > kernel.sym $(OBJDUMP) -t kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > kernel.sym
tags: $(OBJS) bootother.S _init tags: $(OBJS) bootother.S _init
etags *.S *.c etags *.S *.c
@ -80,7 +80,7 @@ ULIB = ulib.o usys.o printf.o umalloc.o
_%: %.o $(ULIB) _%: %.o $(ULIB)
$(LD) -N -e main -Ttext 0 -o $@ $^ $(LD) -N -e main -Ttext 0 -o $@ $^
$(OBJDUMP) -S $@ > $*.asm $(OBJDUMP) -S $@ > $*.asm
$(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* //' > $*.sym $(OBJDUMP) -t $@ | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $*.sym
_forktest: forktest.o $(ULIB) _forktest: forktest.o $(ULIB)
# forktest has less library code linked in - needs to be small # forktest has less library code linked in - needs to be small
@ -119,18 +119,7 @@ clean:
$(UPROGS) $(UPROGS)
# make a printout # make a printout
PRINT = \ PRINT = runoff.list $(shell grep -v '^\#' runoff.list)
runoff.list \
README\
types.h param.h defs.h x86.h asm.h elf.h mmu.h spinlock.h\
bootasm.S bootother.S main.c init.c spinlock.c\
proc.h proc.c setjmp.S kalloc.c\
syscall.h trapasm.S traps.h trap.c vectors.pl syscall.c sysproc.c\
buf.h dev.h fcntl.h stat.h file.h fs.h fsvar.h file.c fs.c bio.c ide.c sysfile.c\
pipe.c exec.c\
mp.h ioapic.h mp.c lapic.c ioapic.c picirq.c\
console.c\
string.c\
xv6.pdf: $(PRINT) xv6.pdf: $(PRINT)
./runoff ./runoff

7
defs.h
View file

@ -1,7 +1,7 @@
struct buf; struct buf;
struct context;
struct file; struct file;
struct inode; struct inode;
struct jmpbuf;
struct pipe; struct pipe;
struct proc; struct proc;
struct spinlock; struct spinlock;
@ -109,9 +109,8 @@ void userinit(void);
void wakeup(void*); void wakeup(void*);
void yield(void); void yield(void);
// setjmp.S // swtch.S
void longjmp(struct jmpbuf*); void swtch(struct context*, struct context*);
int setjmp(struct jmpbuf*);
// spinlock.c // spinlock.c
void acquire(struct spinlock*); void acquire(struct spinlock*);

16
proc.c
View file

@ -134,10 +134,10 @@ copyproc(struct proc *p)
np->cwd = idup(p->cwd); np->cwd = idup(p->cwd);
} }
// Set up new jmpbuf to start executing at forkret (see below). // Set up new context to start executing at forkret (see below).
memset(&np->jmpbuf, 0, sizeof(np->jmpbuf)); memset(&np->context, 0, sizeof(np->context));
np->jmpbuf.eip = (uint)forkret; np->context.eip = (uint)forkret;
np->jmpbuf.esp = (uint)np->tf - 4; np->context.esp = (uint)np->tf;
// Clear %eax so that fork system call returns 0 in child. // Clear %eax so that fork system call returns 0 in child.
np->tf->eax = 0; np->tf->eax = 0;
@ -206,8 +206,7 @@ scheduler(void)
setupsegs(p); setupsegs(p);
cp = p; cp = p;
p->state = RUNNING; p->state = RUNNING;
if(setjmp(&cpus[cpu()].jmpbuf) == 0) swtch(&cpus[cpu()].context, &p->context);
longjmp(&p->jmpbuf);
// Process is done running for now. // Process is done running for now.
// It should have changed its p->state before coming back. // It should have changed its p->state before coming back.
@ -232,8 +231,7 @@ sched(void)
if(cpus[cpu()].nlock != 1) if(cpus[cpu()].nlock != 1)
panic("sched locks"); panic("sched locks");
if(setjmp(&cp->jmpbuf) == 0) swtch(&cp->context, &cpus[cpu()].context);
longjmp(&cpus[cpu()].jmpbuf);
} }
// Give up the CPU for one scheduling round. // Give up the CPU for one scheduling round.
@ -458,7 +456,7 @@ procdump(void)
state = "???"; state = "???";
cprintf("%d %s %s", p->pid, state, p->name); cprintf("%d %s %s", p->pid, state, p->name);
if(p->state == SLEEPING) { if(p->state == SLEEPING) {
getcallerpcs((uint*)p->jmpbuf.ebp+2, pc); getcallerpcs((uint*)p->context.ebp+2, pc);
for(j=0; j<10 && pc[j] != 0; j++) for(j=0; j<10 && pc[j] != 0; j++)
cprintf(" %p", pc[j]); cprintf(" %p", pc[j]);
} }

16
proc.h
View file

@ -10,18 +10,18 @@
// Don't need to save all the %fs etc. segment registers, // Don't need to save all the %fs etc. segment registers,
// because they are constant across kernel contexts. // because they are constant across kernel contexts.
// Save all the regular registers so we don't need to care // Save all the regular registers so we don't need to care
// which are caller save. // which are caller save, but not the return register %eax.
// Don't save %eax, because that's the return register. // (Not saving %eax just simplifies the switching code.)
// The layout of jmpbuf must match code in setjmp.S. // The layout of context must match code in swtch.S.
struct jmpbuf { struct context {
int eip;
int esp;
int ebx; int ebx;
int ecx; int ecx;
int edx; int edx;
int esi; int esi;
int edi; int edi;
int esp;
int ebp; int ebp;
int eip;
}; };
enum proc_state { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; enum proc_state { UNUSED, EMBRYO, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
@ -38,7 +38,7 @@ struct proc {
int killed; // If non-zero, have been killed int killed; // If non-zero, have been killed
struct file *ofile[NOFILE]; // Open files struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory struct inode *cwd; // Current directory
struct jmpbuf jmpbuf; // Jump here to run process struct context context; // Switch here to run process
struct trapframe *tf; // Trap frame for current interrupt struct trapframe *tf; // Trap frame for current interrupt
char name[16]; // Process name (debugging) char name[16]; // Process name (debugging)
}; };
@ -61,7 +61,7 @@ extern struct proc *curproc[NCPU]; // Current (running) process per CPU
// Per-CPU state // Per-CPU state
struct cpu { struct cpu {
uchar apicid; // Local APIC ID uchar apicid; // Local APIC ID
struct jmpbuf jmpbuf; // Jump here to enter scheduler struct context context; // Switch here to enter scheduler
struct taskstate ts; // Used by x86 to find stack for interrupt struct taskstate ts; // Used by x86 to find stack for interrupt
struct segdesc gdt[NSEGS]; // x86 global descriptor table struct segdesc gdt[NSEGS]; // x86 global descriptor table
char mpstack[MPSTACK]; // Per-CPU startup stack char mpstack[MPSTACK]; // Per-CPU startup stack

View file

@ -22,7 +22,7 @@ spinlock.c
# processes # processes
proc.h proc.h
proc.c proc.c
setjmp.S swtch.S
kalloc.c kalloc.c
# system calls # system calls

View file

@ -1,49 +0,0 @@
# int setjmp(struct jmpbuf *jmp);
# void longjmp(struct jmpbuf *jmp);
#
# Setjmp saves its stack environment in jmp for later use by longjmp.
# It returns 0.
#
# Longjmp restores the environment saved by the last call of setjmp.
# It then causes execution to continue as if the call of setjmp
# had just returned 1.
#
# The caller of setjmp must not itself have returned in the interim.
# All accessible data have values as of the time longjmp was called.
#
# [Description, but not code, borrowed from Plan 9.]
.globl setjmp
setjmp:
movl 4(%esp), %eax
movl %ebx, 0(%eax)
movl %ecx, 4(%eax)
movl %edx, 8(%eax)
movl %esi, 12(%eax)
movl %edi, 16(%eax)
movl %esp, 20(%eax)
movl %ebp, 24(%eax)
pushl 0(%esp) # %eip
popl 28(%eax)
movl $0, %eax # return value
ret
.globl longjmp
longjmp:
movl 4(%esp), %eax
movl 0(%eax), %ebx
movl 4(%eax), %ecx
movl 8(%eax), %edx
movl 12(%eax), %esi
movl 16(%eax), %edi
movl 20(%eax), %esp
movl 24(%eax), %ebp
addl $4, %esp # pop and discard %eip
pushl 28(%eax) # push new %eip
movl $1, %eax # return value (appears to come from setjmp!)
ret

32
swtch.S Normal file
View file

@ -0,0 +1,32 @@
# void swtch(struct context *old, struct context *new);
#
# Save current register context in old
# and then load register context from new.
.globl swtch
swtch:
# Save old registers
movl 4(%esp), %eax
popl 0(%eax) # %eip
movl %esp, 4(%eax)
movl %ebx, 8(%eax)
movl %ecx, 12(%eax)
movl %edx, 16(%eax)
movl %esi, 20(%eax)
movl %edi, 24(%eax)
movl %ebp, 28(%eax)
# Load new registers
movl 4(%esp), %eax # not 8(%esp) - popped return address above
movl 28(%eax), %ebp
movl 24(%eax), %edi
movl 20(%eax), %esi
movl 16(%eax), %edx
movl 12(%eax), %ecx
movl 8(%eax), %ebx
movl 4(%eax), %esp
pushl 0(%eax) # %eip
ret