The memory subsystem (helix-memory, 12 files , ~2,200 lines ) manages all physical and virtual memory from early boot through full runtime.
Memory Subsystem — Architecture 5N · 4E ▶ AddressSpace Per-process virtual … 1 PageTableMapper Architecture-generic… 2 HeapAllocator / GlobalHeap Kernel heap (GlobalA… 2 PhysicalMemoryManager Global PMM 2 ■ RegionManager + ProtectionDomain RAM / ROM / Device /… 1
☝ Drag to pan · 🤏 Pinch to zoom · Tap a node
Ctrl+F Search
P Path
S Stats
F Fullscreen
E Export
Shift+Drag Move node
↑↓ Navigate
+/− Zoom
subsystems/memory/src/lib.rs
address : PhysAddr , // Physical address of the frame
size : PageSize , // Size (4K / 2M / 1G)
address : VirtAddr , // Virtual address of the page
size : PageSize , // Size (4K / 2M / 1G)
Dma , // 0-16 MB (ISA DMA)
Dma32 , // 0-4 GB (32-bit device DMA)
Normal , // 4 GB+ (general purpose)
High , // Above addressable range
Device , // Memory-mapped I/O
Allocator Algorithm Block Size Use Case BumpAllocatorLinear bump pointer 4 KB frames Early boot only — fast, never frees BitmapAllocatorBit-per-frame bitmap 4 KB frames General allocation — find_free, zone support BuddyAllocatorBuddy system 4 KB → 8 MB Large contiguous allocations — MAX_ORDER=11
The BuddyAllocator splits and merges power-of-2 sized blocks:
Buddy Allocator — Order Sizes 8N · 7E merge merge merge merge merge ... merge ▶ Order 0 4 KB (1 page) 1 Order 1 8 KB (2 pages) 2 Order 2 16 KB (4 pages) 2 Order 3 32 KB 2 Order 4 64 KB 2 Order 5 128 KB 2 Order 10 4 MB (1024 pages) 2 ■ Order 11 8 MB (2048 pages) 1
☝ Drag to pan · 🤏 Pinch to zoom · Tap a node
Ctrl+F Search
P Path
S Stats
F Fullscreen
E Export
Shift+Drag Move node
↑↓ Navigate
+/− Zoom
When a 16 KB allocation is requested, the allocator finds the smallest available block >= 16 KB and splits larger blocks in half until the right size is reached. When freed, adjacent buddies are merged back together.
For kernel heap allocations (small objects), the SlabAllocator provides 8 size classes :
Class Object Size Slab Layout 0 16 bytes Free-list of 16B cells 1 32 bytes Free-list of 32B cells 2 64 bytes Free-list of 64B cells 3 128 bytes Free-list of 128B cells 4 256 bytes Free-list of 256B cells 5 512 bytes Free-list of 512B cells 6 1024 bytes Free-list of 1KB cells 7 2048 bytes Free-list of 2KB cells
Each size class maintains a linked list of slabs. Allocation is O(1) — pop from the free list. Objects larger than 2 KB fall through to the buddy allocator.
subsystems/memory/src/virtual_memory/mod.rs
pub trait VirtualMapper : Send + Sync {
fn map ( & self , page : Page , frame : Frame , flags : PageFlags ) -> MemResult < ( ) > ;
fn unmap ( & self , page : Page ) -> MemResult < Frame > ;
fn translate ( & self , virt : VirtAddr ) -> Option < PhysAddr > ;
fn update_flags ( & self , page : Page , flags : PageFlags ) -> MemResult < ( ) > ;
fn flush ( & self , page : Page ) ;
pub struct PageFlags : u64 {
const WRITE_THROUGH = 1 << 3 ;
const NO_EXECUTE = 1 << 63 ;
Index VirtualMapper map unmap translate update_flags flush flush_all PageFlags
Each process gets its own AddressSpace providing POSIX-like memory operations:
subsystems/memory/src/virtual_memory/address_space.rs
pub fn mmap_anonymous ( & self , addr : Option < VirtAddr > ,
size : usize , flags : PageFlags ) -> MemResult < VirtAddr > ;
pub fn munmap ( & self , addr : VirtAddr , size : usize ) -> MemResult < ( ) > ;
pub fn mprotect ( & self , addr : VirtAddr , size : usize ,
flags : PageFlags ) -> MemResult < ( ) > ;
pub fn brk ( & self , new_brk : VirtAddr ) -> MemResult < VirtAddr > ;
Index AddressSpace mmap_anonymous munmap mprotect brk
Region management detects overlaps and automatically splits/merges adjacent regions with compatible flags.
subsystems/memory/src/protection.rs
pub struct ProtectionDomain {
pub key : ProtectionKey , // Intel PKU key (0-15)
pub flags : ProtectionFlags ,
pub struct ProtectionFlags : u32 {
const RW = Self :: READ . bits ( ) | Self :: WRITE . bits ( ) ;
const RX = Self :: READ . bits ( ) | Self :: EXECUTE . bits ( ) ;
const RWX = Self :: RW . bits ( ) | Self :: EXECUTE . bits ( ) ;
Index ProtectionDomain ProtectionFlags
GuardPage : Unmapped pages placed around kernel stacks to catch stack overflows. A page fault on a guard page triggers an immediate panic with a clear stack overflow message.
The execution subsystem (helix-execution, 12 files , ~2,500 lines ) manages threads, processes, scheduling, and context switching.
subsystems/execution/src/thread/thread.rs
state : AtomicU32 , // ThreadState
priority : spin :: RwLock < Priority > ,
flags : spin :: RwLock < ThreadFlags > ,
context : spin :: Mutex < ThreadContext > ,
kernel_stack : KernelStack , // 16 KiB
affinity : spin :: RwLock < u64 > , // CPU bitmask
cpu : spin :: RwLock < Option < usize >> ,
exit_code : spin :: RwLock < Option < i32 >> ,
Creating = 0 , // Being set up
Ready = 1 , // Runnable, waiting for CPU
Running = 2 , // Currently executing
Blocked = 3 , // Waiting for event/resource
Sleeping = 4 , // Timed wait
Stopped = 5 , // Stopped by signal/debugger
Dead = 6 , // Fully cleaned up
Zombie = 7 , // Terminated, not yet reaped
The hardware context saved/restored during context switches:
subsystems/execution/src/thread/thread.rs
pub struct ThreadContext {
pub ip : u64 , // Instruction pointer
pub sp : u64 , // Stack pointer
pub regs : [ u64 ; 16 ] , // General-purpose registers
pub flags : u64 , // CPU flags (RFLAGS on x86)
FPU state is saved lazily — only when a thread that used FPU/SSE/AVX is switched out and another thread needs FPU. This avoids the cost of saving 512+ bytes of XMM/YMM registers on every context switch.
subsystems/execution/src/scheduler/traits.rs
pub trait Scheduler : Send + Sync {
fn name ( & self ) -> & 'static str ;
fn version ( & self ) -> & 'static str ;
fn init ( & mut self , cpu_count : usize ) -> ExecResult < ( ) > ;
fn pick_next ( & self , cpu : usize ) -> Option < ThreadId > ;
fn add_thread ( & self , thread : SchedulableThread ) -> ExecResult < ( ) > ;
fn remove_thread ( & self , id : ThreadId ) -> ExecResult < ( ) > ;
fn thread_ready ( & self , id : ThreadId ) -> ExecResult < ( ) > ;
fn thread_block ( & self , id : ThreadId ) -> ExecResult < ( ) > ;
fn yield_thread ( & self , cpu : usize ) ;
fn tick ( & self , cpu : usize ) ;
fn set_priority ( & self , id : ThreadId , priority : Priority ) -> ExecResult < ( ) > ;
fn needs_reschedule ( & self , cpu : usize ) -> bool ;
fn stats ( & self ) -> SchedulerStats ;
Index Scheduler name version init pick_next add_thread remove_thread thread_ready thread_block yield_thread tick set_priority needs_reschedule stats
Three scheduling queue types are available:
Queue Algorithm Use Case FifoQueueFirst-in-first-out Round-robin scheduling PriorityQueuePriority-sorted Real-time threads MultilevelQueueMultiple priority bands General purpose
subsystems/execution/src/scheduler.rs
pub struct Priority ( pub u8 ) ; // 0-139
pub const REALTIME_THRESHOLD : u8 = 100 ;
pub const IDLE : Priority = Priority ( 0 ) ;
pub const LOW : Priority = Priority ( 30 ) ;
pub const NORMAL : Priority = Priority ( 60 ) ;
pub const HIGH : Priority = Priority ( 90 ) ;
pub const REALTIME : Priority = Priority ( 120 ) ;
pub const CRITICAL : Priority = Priority ( 139 ) ;
Index Priority Priority REALTIME_THRESHOLD IDLE LOW NORMAL HIGH REALTIME CRITICAL
Threads with priority >= 100 are treated as real-time and always preempt non-real-time threads.
subsystems/execution/src/context.rs
pub struct ContextSwitchEngine {
hooks : spin :: RwLock < Vec < Arc < dyn ContextSwitchHook >> > ,
impl ContextSwitchEngine {
pub fn add_hook ( & self , hook : Arc < dyn ContextSwitchHook > ) ;
& self , from : ThreadId , to : ThreadId ,
from_ctx : & mut ThreadContext , to_ctx : & ThreadContext ,
Yield , // Voluntary yield
Preemption , // Timer quantum expired
Blocked , // Waiting for resource
Exit , // Thread terminating
Interrupt , // Interrupt handling
NewThread , // New thread started
Index ContextSwitchEngine ContextSwitchEngine add_hook switch SwitchReason
Hooks are called before and after each context switch, enabling:
Statistics collection — track CPU time per thread
TLS switching — update thread-local storage pointer
FPU lazy switching — save FPU state only when needed
Performance counters — reset per-thread PMC values
subsystems/execution/src/process.rs
parent : Option < ProcessId > ,
state : AtomicU32 , // ProcessState
main_thread : RwLock < Option < ThreadId >> ,
threads : RwLock < Vec < ThreadId >> ,
children : RwLock < Vec < ProcessId >> ,
exit_code : RwLock < Option < i32 >> ,
Creating , Running , Stopped , Zombie , Dead ,
subsystems/execution/src/domains.rs
Kernel , // Ring 0, full privileges
User , // Ring 3, standard process
Driver , // May be ring 0 or separate
Sandbox , // Restricted user
pub struct ExecutionDomain {
threads : RwLock < BTreeSet < ThreadId >> ,
processes : RwLock < BTreeSet < ProcessId >> ,
Index DomainType ExecutionDomain
The Dynamic Intent Scheduling system (helix-dis, 12 files , ~11,600 lines ) is Helix's unique scheduling approach that schedules tasks based on intent rather than just priority.
Instead of assigning a static priority, each task declares an intent describing what it's trying to accomplish. The DIS engine uses this intent to make scheduling decisions, adjust time slices, and optimize for the workload:
RealTime = 0 , // Audio, video — strict deadlines
SoftRealTime = 1 , // UI rendering — best-effort deadlines
Interactive = 2 , // User-facing — low latency
Server = 3 , // Request-response — throughput
Batch = 4 , // Data processing — CPU-bound
Background = 5 , // Maintenance — lowest priority
Idle = 6 , // Run only when nothing else needs CPU
Critical = 7 , // Kernel services — highest priority
pub latency : LatencyTarget ,
pub cpu_budget : CpuBudget ,
pub memory_budget : MemoryBudget ,
pub quota : Duration , // Max CPU time per period
pub period : Duration , // Budget period
DIS provides built-in presets for common workloads:
Preset Intent Class Time Slice CPU Affinity audio_playbackRealTime 1 ms Preferred single core video_renderingRealTime 2 ms Any ui_threadInteractive 4 ms Preferred BSP web_serverServer 10 ms Any databaseServer 8 ms NUMA-aware compilationBatch 50 ms All cores backupBackground 100 ms Any gcBackground 20 ms Any monitoringIdle 200 ms Any
DIS integrates 7 queue levels into a unified scheduling framework:
DIS — Queue Levels 7N · 6E fallthrough fallthrough fallthrough fallthrough fallthrough fallthrough ▶ Level 0: RealTime DeadlineQueue 1 Level 1: SoftRealTime DeadlineQueue 2 Level 2: Interactive FairQueue 2 Level 3: Server FairQueue 2 Level 4: Batch MultilevelFeedbackQu… 2 Level 5: Background MultilevelFeedbackQu… 2 ■ Level 6: Idle FIFO 1
☝ Drag to pan · 🤏 Pinch to zoom · Tap a node
Ctrl+F Search
P Path
S Stats
F Fullscreen
E Export
Shift+Drag Move node
↑↓ Navigate
+/− Zoom
The IntegratedQueues struct manages all seven levels. On each tick, the scheduler picks the highest-priority non-empty queue and selects the next task from it.
DIS includes a machine-learning-based prediction engine that learns from task behavior:
subsystems/dis/optimizer.rs
pub struct PredictionEngine {
pub fn predict_runtime ( & self , task : & TaskDescriptor ) -> Duration ;
pub fn predict_memory ( & self , task : & TaskDescriptor ) -> usize ;
pub fn suggest_strategy ( & self , task : & TaskDescriptor ) -> OptimizationHint ;
pub fn learn ( & mut self , task : & TaskDescriptor , actual : & TaskStatistics ) ;
Index PredictionEngine predict_runtime predict_memory suggest_strategy learn
Optimization strategies include:
TimeSlice — adjust quantum based on predicted runtime
Priority — boost/demote based on behavior pattern
Affinity — pin to optimal CPU based on cache locality
Energy — consolidate tasks for power saving
DIS enforces isolation at the scheduling level:
subsystems/dis/isolation.rs
pub struct SecurityDomain {
pub domain_type : DomainType ,
pub default_caps : CapabilitySet ,
pub isolation : IsolationLevel ,
pub limits : ResourceLimits ,
Kernel , System , User , Service , Sandbox , Guest , Container ,
pub enum IsolationLevel {
None , // No isolation — kernel threads
Light , // Basic process isolation
Standard , // Full memory protection
Strict , // Verified IPC only
Paranoid , // Maximum restrictions, minimal capabilities
Index SecurityDomain DomainType IsolationLevel
44 distinct capabilities are managed via a 128-bit bitmap for O(1) permission checks.
pub cpu_time : Nanoseconds ,
pub user_time : Nanoseconds ,
pub kernel_time : Nanoseconds ,
pub wait_time : Nanoseconds ,
pub io_wait_time : Nanoseconds ,
pub context_switches : u64 ,
pub voluntary_switches : u64 ,
pub involuntary_switches : u64 ,
pub enum BehaviorPattern {
CpuBound , IoBound , Mixed , Bursty ,
Periodic , EventDriven , Latency , Throughput ,
Index TaskStats BehaviorPattern
The StatsCollector performs anomaly analysis — detecting tasks that deviate from expected behavior patterns and flagging them for policy review.
The init subsystem (helix-init, 23 files , ~4,500 lines ) manages the kernel's 5-phase boot sequence.
subsystems/init/src/phase.rs
Boot = 0 , // Firmware handoff, serial console
Early = 1 , // GDT, IDT, heap, physical memory
Core = 2 , // Interrupts, timers, scheduler
Late = 3 , // Drivers, filesystem, IPC
Runtime = 4 , // Userspace, modules, networking
Each phase unlocks new capabilities tracked via bitflags:
subsystems/init/src/phase.rs
pub struct PhaseCapabilities : u32 {
const INTERRUPTS = 1 << 3 ;
const SCHEDULER = 1 << 4 ;
const FILESYSTEM = 1 << 8 ;
const USERSPACE = 1 << 9 ;
A subsystem can only be initialized if all its required capabilities are available. The init framework enforces this at runtime.
subsystems/init/src/subsystem.rs
pub struct SubsystemInfo {
pub priority : u32 , // Within-phase ordering
pub dependencies : Vec < & 'static str > ,
pub capabilities : PhaseCapabilities , // What this provides
pub requires : PhaseCapabilities , // What this needs
pub trait Subsystem : Send + Sync {
fn name ( & self ) -> & 'static str ;
fn init ( & mut self ) -> Result < ( ) > ;
fn start ( & mut self ) -> Result < ( ) > ;
fn stop ( & mut self ) -> Result < ( ) > ;
fn health_check ( & self ) -> HealthStatus ;
fn state ( & self ) -> SubsystemState ;
fn dependencies ( & self ) -> & [ & 'static str ] ;
fn capabilities ( & self ) -> PhaseCapabilities ;
pub enum SubsystemState {
Registered , Initializing , Running , Stopping ,
Stopped , Failed , Recovering ,
Index SubsystemInfo Subsystem name init start stop health_check state dependencies capabilities SubsystemState
Subsystem Phase Priority Provides Dependencies boot 0 0 CONSOLE None cpu 1 10 — boot debug 1 20 — boot memory 1 30 HEAP, MEMORY cpu interrupts 2 40 INTERRUPTS memory timers 2 50 TIMERS interrupts scheduler 2 60 SCHEDULER timers, memory ipc 3 70 IPC scheduler drivers 3 80 DRIVERS interrupts, memory filesystem 3 85 FILESYSTEM drivers, memory security 3 90 — memory, ipc network 4 100 NETWORK drivers, ipc userland 4 110 USERSPACE, MODULES filesystem, security, scheduler
The relocation subsystem (helix-relocation, 12 files , ~2,000 lines , edition 2024 ) handles ELF binary relocation for KASLR and PIE kernel images.
When the kernel is built as a Position-Independent Executable (PIE), all absolute addresses in the binary must be adjusted when the kernel is loaded at a different address than it was linked for. This adjustment is called relocation .
Relocation — Source Tree 10N · 9E ▶ helix-relocation/src/ ELF relocation engin… 9 lib.rs Public API, Relocati… 1 elf.rs ELF header/section/s… 1 relocations.rs Relocation entry pro… 1 sections.rs Section header itera… 1 symbols.rs Symbol table lookup 1 kaslr.rs KASLR entropy + offs… 1 validation.rs Post-relocation inte… 1 arch/ Architecture-specifi… 1 boot_context.rs Boot information for… 1
☝ Drag to pan · 🤏 Pinch to zoom · Tap a node
Ctrl+F Search
P Path
S Stats
F Fullscreen
E Export
Shift+Drag Move node
↑↓ Navigate
+/− Zoom
Parse ELF headers from the kernel image
Read the .dynamic section to find .rela.dyn and .rela.plt
Generate KASLR offset from entropy source
Apply each relocation: *(target) = base + addend + kaslr_offset
Validate: ensure all relocated pointers fall within the kernel image
Jump to the relocated kernel entry point
The userspace subsystem (helix-userspace, 8 files , ~3,500 lines ) provides the bridge between kernel services and user programs.
subsystems/userspace/src/elf.rs
pub fn parse ( data : & [ u8 ] ) -> Result < ParsedElf > ;
pub fn load ( parsed : & ParsedElf , address_space : & mut AddressSpace ) -> Result < LoadedElf > ;
pub program_headers : Vec < ProgramHeader > ,
pub section_headers : Vec < SectionHeader > ,
pub symbols : Vec < Symbol > ,
Index ElfLoader ElfLoader parse load ParsedElf
The loader supports:
ELF64 format (little-endian)
PT_LOAD segments for code and data
PT_INTERP for dynamic linking (future)
PIE (Position-Independent Executables)
Section headers for debug info and symbols
subsystems/userspace/src/runtime.rs
pub fn spawn ( & mut self , program : & Program ,
args : & [ & str ] , env : & Environment ) -> Result < ProcessHandle > ;
pub fn kill ( & mut self , pid : ProcessId , signal : Signal ) -> Result < ( ) > ;
pub fn reap ( & mut self , pid : ProcessId ) -> Result < ExitStatus > ;
pub struct ProcessHandle {
Created , Running , Blocked , Stopped , Zombie , Dead ,
Index Runtime spawn kill reap ProcessHandle ProcessState
subsystems/userspace/src/fd.rs
entries : BTreeMap < Fd , FdEntry > ,
pub fn new ( ) -> Self ; // Pre-opens stdin(0), stdout(1), stderr(2)
pub fn alloc ( & mut self , entry : FdEntry ) -> Result < Fd > ;
pub fn close ( & mut self , fd : Fd ) -> Result < ( ) > ;
pub fn dup ( & mut self , old_fd : Fd ) -> Result < Fd > ;
pub fn get ( & self , fd : Fd ) -> Option < & FdEntry > ;
Index FdTable FdTable new alloc close dup get
The built-in kernel shell provides 16 commands :
Command Description helpList all commands exitExit the shell echo <text>Print text to stdout clearClear the screen psList running processes and threads memDisplay memory statistics uptimeShow kernel uptime unamePrint kernel name and version set <key>=<val>Set environment variable historyShow command history benchRun benchmarks statsDisplay system statistics cat <file>Print file contents run <program>Execute an ELF binary versionPrint detailed version info demoRun kernel demonstration
The shell supports ANSI color output, command history, and an ASCII art banner on startup.
The userspace crate implements 47 syscalls (42 POSIX-compatible + 5 Helix-specific):
POSIX Syscalls:
Number Name Description 0 readRead from file descriptor 1 writeWrite to file descriptor 2 openOpen file 3 closeClose file descriptor 4 statGet file status 9 mmapMap memory 10 mprotectChange memory protection 11 munmapUnmap memory 12 brkChange data segment size 39 getpidGet process ID 57 forkCreate child process 59 execveExecute program 60 exitTerminate process 62 killSend signal
Helix-Specific Syscalls:
Number Name Description 500 helix_dis_statsGet DIS scheduler statistics 501 helix_hot_reloadTrigger module hot-reload 502 helix_self_healTrigger subsystem recovery 503 helix_benchmarkRun kernel benchmarks 504 helix_kernel_infoGet kernel version and state
subsystems/userspace/src/syscall.rs
pub number : usize , // rax
// Return value in rax. Error: negative errno.
The early boot path bridges the gap between the bootloader and the full kernel. This code runs before the heap, interrupts, or scheduler are available.
The helix-minimal-os crate (profiles/minimal/main.rs, ~1,700 lines ) is the kernel entry point:
profiles/minimal/src/main.rs pub extern "C" fn kernel_main ( boot_info : & BootInfo ) -> ! {
kprintln! ( "Helix OS v{}" , VERSION ) ;
heap :: init ( HEAP_BASE , HEAP_SIZE ) ; // 4 MB initial heap
memory :: init ( boot_info . memory_map ( ) ) ;
Protocol Entry Symbol Boot Info Paging Limine limine_entryLimine Protocol Rev 2 requests Higher-half provided Multiboot2 _startMultiboot2 info structure Must set up manually UEFI efi_mainUEFI System Table Identity-mapped, must remap
Each protocol provides a memory map, framebuffer info, RSDP pointer, and command line. The early boot code normalizes this into a common BootConfiguration.