Memory Fundamentals

Lesson, slides, and applied problem sets.

View Slides

Lesson

Memory Fundamentals

Understanding how memory works at the OS level is essential before building a memory allocator.

Process Memory Layout

A process's memory is divided into segments:

High addresses
+------------------+
|      Stack       |  ← Grows downward
|        ↓         |
+------------------+
|                  |
|   Unused space   |
|                  |
+------------------+
|        ↑         |
|       Heap       |  ← Grows upward
+------------------+
|       BSS        |  ← Uninitialized globals
+------------------+
|       Data       |  ← Initialized globals
+------------------+
|       Text       |  ← Program code
+------------------+
Low addresses

The Heap

The heap is where dynamic memory allocation happens. Key properties:

  1. Contiguous region - The heap is one continuous block of memory
  2. Grows upward - Toward higher addresses
  3. Bounded by program break - The end of the heap is called the "program break"

The sbrk System Call

sbrk manipulates the program break:

#include <unistd.h>

// Get current program break
void *current = sbrk(0);

// Extend heap by 1000 bytes
void *old_break = sbrk(1000);  // Returns OLD break
// old_break now points to start of new 1000 bytes

// Check for failure
if (old_break == (void*)-1) {
    // sbrk failed! (not NULL, but -1 cast to void*)
}

Why Not Use sbrk Directly?

You could use sbrk for every allocation:

void *my_simple_alloc(size_t size) {
    return sbrk(size);
}

Problems:

  1. No free() - You can only shrink the heap from the top
  2. No reuse - Freed memory in the middle is wasted
  3. No metadata - You don't know how big each allocation is

This is why we need a real allocator.

Modern Alternatives

While sbrk is traditional, modern allocators also use:

  • mmap - Map anonymous memory pages
  • brk - Set program break directly (sbrk is built on this)

For learning, sbrk is simpler and sufficient.


Module Items