Trap Handling
This lecture goes over proc.h
which focuses on process management in
and data structures to handle processes.
Trap Handling
When a trap occurs, the CPU switches to kernel mode, disables interrupts, saves user registers, and then processes the trap, before restoring registers and returning control.
Execution Flow from Trap to Sret
Trap Handling Begins:
- The hardware switches to supervisor mode.
- The CPU saves:
- Program counter (PC) →
sepc
- Trap cause →
scause
- Jumps to uservec function (located in the trampoline page).
- Program counter (PC) →
Saving User Context (uservec in trampoline page):
- Saves all user registers into the trap frame.
- Initializes the kernel stack pointer and core number (tp register).
- Switches to the kernel’s virtual address space (modifies satp).
- Jumps into the C function usertrap().
Processing the Trap (usertrap() in C)
- Updates stvec to point to the correct exception handler.
- Reads scause to determine the type of trap:
- System Call → Processes the syscall.
- Hardware Interrupt → Handles device or timer interrupts.
- Program Error → Terminates the process (exit()).
- If the time slice ends, the process is preempted and another process is scheduled.
Returning to User Mode (usertrapret() in C)
- Disables interrupts before returning.
- Resets
stvec
to point touservec
for future traps. - Restores the user’s page table (modifies
satp
). - Loads the user’s saved registers.
- Executes
sret
, restoring user mode execution.
Key Data Structures
The trapframe
struct stores the 31 GPRs from x1 to x31, saves the
program counter (sepc
), stack pointer, sp
, and hartid
for the
running CPU.
CPU struct
- There are 8
cpu
structs, one for each core.
It outlines the pointer to the currently running process, the context (where registers are saved), and the interrupt state.
// Per-CPU state.
struct cpu {
struct proc *proc; // The process running on this cpu, or null.
struct context context; // swtch() here to enter scheduler().
int noff; // Depth of push_off() nesting.
int intena; // Were interrupts enabled before push_off()?
};
extern struct cpu cpus[NCPU];
Process State
A process has a corresponding Proc structure:
-
pid
Process ID -
state
Process state -
parent
pointer to parent process -
pagetable
pointer to the user page table -
kstack
kernel stack pointer -
trampframe
pointer to trap frame for user registers -
context
CPU register save area -
ofile
open files -
cwd
current working directory -
name
process name
And a corresponding proc state for each process.
enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE };
// Per-process state
struct proc {
struct spinlock lock;
// p->lock must be held when using these:
enum procstate state; // Process state
void *chan; // If non-zero, sleeping on chan
int killed; // If non-zero, have been killed
int xstate; // Exit status to be returned to parent's wait
int pid; // Process ID
// wait_lock must be held when using this:
struct proc *parent; // Parent process
// these are private to the process, so p->lock need not be held.
uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
pagetable_t pagetable; // User page table
struct trapframe *trapframe; // data page for trampoline.S
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
char name[16]; // Process name (debugging)
};