Skip to main content
The heap configuration defines the memory region used for dynamic allocation in Portix OS. These constants control the buddy allocator’s behavior and memory layout.

Location

kernel/src/mem/mod.rs

Memory Layout Constants

HEAP_START

Starting physical address of the kernel heap.
pub const HEAP_START: usize = 0x0100_0000;
value
usize
0x0100_0000 (16 MiB) - Heap begins at the 16 MiB mark in physical memory.
This address is chosen to avoid:
  • Low memory (0-1 MiB): BIOS data, interrupt vectors, bootloader
  • Kernel code and data (1-16 MiB typically)

HEAP_SIZE

Total size of the kernel heap in bytes.
pub const HEAP_SIZE: usize = 64 * 1024 * 1024;
value
usize
67,108,864 bytes (64 MiB) - Total heap capacity.
The heap occupies the range 0x0100_0000 to 0x0500_0000 (16 MiB to 80 MiB).

Order Constants

The buddy allocator uses “orders” as power-of-two block sizes.

MIN_ORDER

Smallest allocatable block order.
pub const MIN_ORDER: usize = 4;
value
usize
Order 4 = 2^4 = 16 bytes minimum allocation.
Allocations smaller than 16 bytes are rounded up to 16 bytes. This minimum prevents excessive fragmentation and ensures enough space for free list node metadata.

MAX_ORDER

Largest allocatable block order.
pub const MAX_ORDER: usize = 22;
value
usize
Order 22 = 2^22 = 4,194,304 bytes (4 MiB) maximum single allocation.
Allocations larger than 4 MiB will fail. This is a design trade-off:
  • Larger MAX_ORDER = more memory per allocation, but more internal fragmentation
  • Smaller MAX_ORDER = less waste, but cannot satisfy large allocations

ORDER_COUNT

Total number of order levels maintained by the allocator.
pub const ORDER_COUNT: usize = MAX_ORDER - MIN_ORDER + 1;
value
usize
19 orders (4 through 22 inclusive)
This defines the size of the free_lists array in BuddyInner.

Order to Size Reference

Each order corresponds to a power-of-two block size:
OrderSize (bytes)Size (human)
41616 B
53232 B
66464 B
7128128 B
8256256 B
9512512 B
101,0241 KiB
112,0482 KiB
124,0964 KiB
138,1928 KiB
1416,38416 KiB
1532,76832 KiB
1665,53664 KiB
17131,072128 KiB
18262,144256 KiB
19524,288512 KiB
201,048,5761 MiB
212,097,1522 MiB
224,194,3044 MiB

Global Allocator Setup

To use the buddy allocator as the global allocator:
use crate::mem::allocator::BuddyAllocator;

#[global_allocator]
static ALLOCATOR: BuddyAllocator = BuddyAllocator::new();

pub fn init() {
    unsafe {
        ALLOCATOR.init();
    }
}
Once initialized, Rust’s allocation APIs work automatically:
use alloc::vec::Vec;
use alloc::string::String;
use alloc::boxed::Box;

pub fn example() {
    // Uses BuddyAllocator under the hood
    let mut v = Vec::new();
    v.push(42);
    
    let s = String::from("Hello, Portix!");
    let boxed = Box::new([1, 2, 3, 4, 5]);
}

Helper Functions

alloc_stats_free_total()

Calculates the total number of free blocks across all orders.
pub fn alloc_stats_free_total() -> usize
returns
usize
Total count of free blocks in the allocator.

Implementation

use core::sync::atomic::Ordering;
use crate::mem::allocator::ALLOC_STATS;

pub fn alloc_stats_free_total() -> usize {
    let mut total = 0usize;
    for cell in &ALLOC_STATS.free_blocks {
        total += cell.load(Ordering::Relaxed);
    }
    total
}

Usage Example

use crate::mem::alloc_stats_free_total;

pub fn check_heap_health() {
    let free_blocks = alloc_stats_free_total();
    if free_blocks == 0 {
        println!("WARNING: Heap exhausted!");
    } else {
        println!("Heap health: {} free blocks", free_blocks);
    }
}

Memory Map Example

Typical Portix OS memory layout:
0x0000_0000 ┌─────────────────────┐
            │ BIOS / IVT          │  Low memory
0x0010_0000 ├─────────────────────┤
            │ Kernel Code         │  Loaded by bootloader
            │ Kernel Data         │
            │ BSS                 │
0x0100_0000 ├─────────────────────┤ ◄─── HEAP_START
            │                     │
            │   BUDDY ALLOCATOR   │  64 MiB heap
            │   (Dynamic Heap)    │
            │                     │
0x0500_0000 ├─────────────────────┤ ◄─── HEAP_START + HEAP_SIZE
            │                     │
            │   (Reserved for     │  Future use
            │    other purposes)  │
            │                     │

Configuration Guidelines

Adjusting Heap Size

To change heap size:
// For 128 MiB heap:
pub const HEAP_SIZE: usize = 128 * 1024 * 1024;

// For 256 MiB heap:
pub const HEAP_SIZE: usize = 256 * 1024 * 1024;
Ensure:
  • Total size is a power of two (or sum of powers of two)
  • Does not overlap with kernel or hardware regions
  • System has sufficient physical RAM

Adjusting Order Range

To support larger single allocations:
// Allow up to 16 MiB allocations:
pub const MAX_ORDER: usize = 24;  // 2^24 = 16 MiB
To reduce minimum allocation size:
// Allow 8-byte minimum:
pub const MIN_ORDER: usize = 3;  // 2^3 = 8 bytes
Note: Smaller MIN_ORDER increases metadata overhead.

Performance Considerations

Internal Fragmentation

Worst-case internal fragmentation approaches 50% when allocations are just over half a block size:
// Request 33 bytes:
// - Allocator rounds to order 6 (64 bytes)
// - Wastes 31 bytes (48% internal fragmentation)

External Fragmentation

The buddy system eliminates external fragmentation through coalescing:
  • When a block is freed, it merges with its buddy
  • Repeated alloc/free cycles return to initial state

Cache Effects

The allocator uses prefetch hints on x86_64:
#[cfg(target_arch = "x86_64")]
core::arch::x86_64::_mm_prefetch(
    buddy_ptr as *const i8,
    core::arch::x86_64::_MM_HINT_T0,
);
This brings the buddy block into L1 cache before coalescing.
  • BuddyAllocator - Allocator implementation
  • kernel/src/mem/mod.rs:7-11 - Constant definitions
  • kernel/src/mem/allocator.rs:10 - Constant usage

Build docs developers (and LLMs) love