Memory Fundamentals
Lesson, slides, and applied problem sets.
View SlidesLesson
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:
- Contiguous region - The heap is one continuous block of memory
- Grows upward - Toward higher addresses
- 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:
- No free() - You can only shrink the heap from the top
- No reuse - Freed memory in the middle is wasted
- 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 pagesbrk- Set program break directly (sbrk is built on this)
For learning, sbrk is simpler and sufficient.