The emulator development has progressed to the point where process switching is possible through mpmain(), scheduler(), and swtch().
The xv6 code at line 334 of proc.c keeps issuing the sti instruction, and it seems to have entered a loop,
so I’d like to summarize scheduling and context switching here.
The process being switched to performs I/O immediately after being switched in the forkret() and iinit() functions.
Here, it enters a sleep state and appears to be waiting for an interrupt.
proc Structure
In xv6, each process is managed by a proc structure.
context stores registers for context switching.
Segment registers like CS are common across processes, so there’s no need to save them.
EAX, ECS, EDX, etc. are automatically saved on the stack during function calls, so they are not included in context.
The first user process is created by the userinit function and allocproc function.
I’ll note the data for the first user process in the comments of the proc structure.
The trap frame is pushed onto the stack by the hardware and trapasm.S,
and is passed to the trap function.
The return destination of the forkret function is set to trapret.
struct proc {
uint sz; // Size of process memory (bytes)
pde_t* pgdir; // Page table
char *kstack; // Bottom of kernel stack for this process
enum procstate state; // Process state: EMBRYO
int pid; // PID: 1
struct proc *parent; // Parent process: NULL
struct trapframe *tf; // Trap frame
struct context *context; // Context for this process
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory: /
char name[16]; // Process name: initcode
};
struct context {
uint edi;
uint esi;
uint ebx;
uint ebp;
uint eip; // forkret
};
struct trapframe {
// registers as pushed by pusha
uint edi, esi, ebp, oesp, ebx, edx, ecx, eax;
// rest of trap frame
ushort gs, padding1, fs, padding2, es, padding3, ds, padding4;
uint trapno;
// below here defined by x86 hardware
uint err;
uint eip;
ushort cs;
ushort padding5;
uint eflags;
// below here only when crossing rings, such as from user to kernel
uint esp;
ushort ss;
ushort padding6;
};
struct {
struct spinlock lock;
struct proc proc[NPROC];
} ptable;
Kernel Stack
I’ll illustrate the contents of proc->kstack.
In swtch, esp is set as proc->context, and edi, esi, ebx, ebp are popped.
Finally, the eip is restored with the ret instruction.
(lower address)
-------------------
| | <= proc->kstack
| |
-------------------
-------------------
| edi | <= proc->context
| esi |
| ebx |
| ebp |
| eip = forkret |
-------------------
-------------------
| | <= trapret (function pointer)
-------------------
-------------------
| | <= proc->tf
-------------------
<= proc->kstack + KSTACKSIZE(4096)
(upper address)
|—————-| | |