Repository navigation
Process Management
cupid-os implements deferred preemptive multitasking with round-robin scheduling. Up to 32 kernel threads share a flat 32-bit address space, all running in ring 0.
| Property | Value |
|---|---|
| Scheduling algorithm | Round-robin |
| Time slice | 10ms (PIT at 100Hz) |
| Max processes | 32 |
| Address space | Flat, shared (ring 0) |
| Context switch | Pure assembly (context_switch.asm) |
| Preemption model | Deferred (flag-based) |
┌──────────┐
│ READY │◄────────────────────┐
└────┬─────┘ │
│ schedule() │ yield() / timer
▼ │
┌──────────┐ ┌──────┴─────┐
│ RUNNING │──────────────│ READY │
└────┬─────┘ └────────────┘
│ exit() / kill()
▼
┌──────────┐
│TERMINATED│ → stack freed lazily on slot reuse
└──────────┘
| State | Description |
|---|---|
READY |
Waiting to be scheduled |
RUNNING |
Currently executing on CPU |
BLOCKED |
Waiting for an event (future use) |
TERMINATED |
Finished, stack pending cleanup |
cupid-os does not context switch inside interrupt handlers. Instead:
-
PIT IRQ0 fires every 10ms → sets
need_reschedule = 1 - The flag is checked only at safe voluntary points:
- Desktop main loop (before
HLTinstruction) -
process_yield()calls - Idle process loop
- Desktop main loop (before
- If the flag is set,
schedule()is called to switch to the next ready process
This approach avoids the complexity and stack corruption risks of switching inside ISRs.
The context switch is implemented in pure x86 assembly (context_switch.asm):
context_switch(uint32_t *old_esp, uint32_t new_esp, uint32_t new_eip)
- Save callee-saved registers: push EFLAGS, EBX, ESI, EDI, EBP onto current stack
-
Store the resulting ESP into
*old_esp(old process's PCB) -
Load new ESP from
new_espparameter -
Jump to
new_eip(eithercontext_switch_resumeor process entry point)
The context_switch_resume label pops saved registers in reverse order and does ret, returning normally to wherever the process left off.
New processes are set up so that:
- ESP points to a prepared stack with fake saved registers
- EIP is the process entry function
-
process_exit_trampolineis the return address on the stack (auto-cleanup when function returns)
A static array of 32 Process Control Blocks (PCBs):
typedef struct {
uint32_t pid; // Process ID (1-based)
process_state_t state; // READY, RUNNING, BLOCKED, TERMINATED
uint32_t esp; // Saved stack pointer
uint32_t eip; // Saved instruction pointer
uint32_t stack_base; // Bottom of allocated stack
uint32_t stack_size; // Stack size in bytes
char name[32]; // Process name for display
} pcb_t;Processes are indexed by PID - 1.
| PID | Name | Description |
|---|---|---|
| 1 | idle | Always present, never exits. Runs STI; HLT in a loop. |
| 2 | desktop | Main kernel thread, registered via process_register_current()
|
| 3 | terminal | GUI terminal, spawned via process_create()
|
| 4+ | user | Test processes, spawned via spawn command |
- Each process gets its own stack via
kmalloc(stack_size) - Default stack size: 4KB (configurable via
DEFAULT_STACK_SIZE)
- A canary value (
0xDEADC0DE) is placed at the bottom of every stack - Checked on every context switch
- If corrupted → kernel panic with process identification
- When a process is terminated, its stack is not freed immediately (because we may still be using it)
- Stack is freed lazily when the PCB slot is reused for a new process
uint32_t pid = process_create(entry_function, "name", stack_size);- Allocates a stack, sets up initial context
- Process starts in
READYstate - Returns PID (0 on failure)
process_register_current("desktop");Used to register an already-running thread (like the main kernel thread) in the process table.
process_yield();Voluntarily gives up the CPU. Clears the reschedule flag and calls schedule().
process_exit();Marks the process as TERMINATED, defers stack free, and reschedules. Also called automatically when a process's entry function returns (via the exit trampoline).
process_kill(pid);Terminates any process by PID. Cannot kill PID 1 (idle process).
process_list();Prints all processes with PID, state, and name. Used by the ps shell command.
| Command | Description |
|---|---|
ps |
List all processes with PID, state, and name |
kill <pid> |
Terminate a process (cannot kill PID 1) |
spawn [n] |
Create 1–16 test counting processes |
yield |
Voluntarily yield CPU to next process |
> ps
PID STATE NAME
1 READY idle
2 RUNNING desktop
3 READY terminal
> spawn 2
Spawned PID 4
Spawned PID 5
> ps
PID STATE NAME
1 READY idle
2 RUNNING desktop
3 READY terminal
4 READY test
5 READY test
> kill 4
Killing PID 4...
- Architecture - How the scheduler fits into the system
- Desktop Environment - Desktop as PID 2
- Debugging - Stack traces and crash testing
Getting Started
Shell & Scripting
Compilers
- CupidC Compiler
- CupidC Language Reference
- CupidC 2D Graphics Library
- CupidASM Assembler
- Toolchain Bootstrap
- Floating Point
Programs & Docs
GUI
Kernel
Filesystems
Hardware