Process
Process struct
struct Process {
id: usize, // PID (monotonic, never reused)
name: [u8; 16], // display name, NUL-padded
mode: Mode, // Kernel (ring 0) or User (ring 3)
status: Status, // see below
last_rsp: u64, // saved kernel-stack RSP; the resume point
kernel_stack: &'static [u8; 32768], // per-slot kernel stack
ports: [Port; 1], // IPC port (index 0 is the default)
stack_top: u64, // initial user-space RSP
cr3: u64, // physical address of P4 page table (0 = kernel CR3)
sleep_until: u64, // PIT tick to wake from sleep (0 = not sleeping)
}
Status Transitions
┌────────────────────────────────────────────────┐
│ ▼
(new) ──► Ready ◄──── push_msg / wake ──── Blocked ──────► Ready
│ ▲ ▲
│ scheduler picks it │ |
▼ │ |
Running ──── blocking syscall ────────┘ |
│ |
├────────────────────────────────────────────────┘
|
├── kill() ──────────────────────► Dead (slot reaped)
├── crash() ──────────────────────► Crashed (stays, not scheduled)
└── idle() ──────────────────────► Idle (stays, not scheduled)
| Status | Scheduled | Description |
|---|---|---|
Ready |
yes | Runnable, waiting for its turn |
Running |
— | Currently executing on the CPU |
Blocked |
no | Waiting for a message or timer |
Idle |
no | Voluntarily suspended (kernel processes only) |
Crashed |
no | Faulted; not rescheduled but slot preserved for diagnostics |
Dead |
no | Exited; page tables freed immediately in kill(), slot reclaimed on next scheduler pass |
Privilege Modes
Mode |
GDT Ring | CS | SS |
|---|---|---|---|
Kernel |
0 | 0x08 |
0x10 |
User |
3 | 0x1b |
0x23 |
Kernel processes use the same kernel stack as their run stack. User processes carry a separate user-space stack (whose top is stored in stack_top) plus a dedicated kernel stack that the CPU switches to on each syscall/interrupt via TSS RSP0.
Initial Stack Frame
new_process builds the initial iretq frame on the kernel stack top:
high address (kstack_top)
┌───────────────┐
│ SS │ ring-3: 0x23 / ring-0: 0x10
│ RSP │ user stack_top / kstack_top
│ RFLAGS │ 0x202 (IF=1)
│ CS │ ring-3: 0x1b / ring-0: 0x08
│ RIP │ entry point
│ RAX..R15 (×15)│ zeroed general-purpose registers
└───────────────┘ ← last_rsp points here
low address
When the scheduler switches to a new process for the first time, it loads this RSP and the naked ISR exits via iretq, which pops RIP/CS/RFLAGS/RSP/SS and jumps to the entry point.
Page Tables (CR3)
User processes get a dedicated P4 page table created by elf::create_user_page_table, which clones the kernel mappings and adds user-accessible entries for:
0x600_000–0x7FF_FFF— ELF load region0x800_000–0x8FF_FFF— user stacks (one 32 KiB stack per slot)0xA00_000–0xAFF_FFF— optional VGA window (mapped on demand by syscall0x14)0xC00_000–0xFFF_FFF— shared userland heap (4 MiB, mapped atuheap::init)
Kernel processes set cr3 = 0; the scheduler falls back to KERNEL_CR3.
Page table reclamation
When kill(pid) is called, the scheduler calls mem::pages::free_user_page_table(proc.cr3) before marking the process Dead. This returns the P4, P3, and P2 pages (and any VGA P1 installed by map_vram) to the free list inside PAGE_TABLE_POOL, making them available for the next create_user_page_table call. proc.cr3 is zeroed immediately after to prevent a double-free if kill() is called again for the same slot.
Crashed processes (Status::Crashed) are not reclaimed — their slot and page tables are preserved for potential post-mortem inspection and are never scheduled again.