The idea: blocks of 2order pages that split and merge

The kernel manages physical memory in page frames of 4 KiB, numbered by their PFN (page frame number; physical address = PFN × 4096). Most requests need one page, but some need several physically contiguous pages: a DMA buffer for a device that does not go through an IOMMU, a 2 MiB huge page, a kernel stack (16 KiB on x86-64). The allocator must hand out contiguous runs quickly and keep free memory from breaking into useless pieces.

The buddy allocator (mm/page_alloc.c) only deals in blocks of 2order contiguous pages, and every block of order k starts at a PFN that is a multiple of 2k. So each block has exactly one buddy, the other half of the block one order up, at PFN XOR 2k. The animation uses a zone of 32 pages and orders 0–5; Linux uses orders 0–10 (up to 4 MiB).

Reading the canvas

  • Zone (top): one row per order, each block drawn as wide as its pages. Green = free block, a colour = allocated (a red border means unmovable), white outline = split into smaller blocks, yellow = the block being worked on. The bottom row is the 32 page frames; the lighter pages of an allocation are the ones wasted by rounding up.
  • free_area[order] (left): the free list of each order, newest first, with nr_free, and the same counts as /proc/buddyinfo prints them.
  • This step (middle): the calculations of the current allocation, free or compaction.
  • Allocations (right): who owns which pages, how many they asked for and how many are wasted.

Allocation: find the smallest block, split it

  1. Round the request up to a power of two: order = ⌈log2 pages⌉.
  2. __rmqueue_smallest looks at free_area[order]; if it is empty, at free_area[order + 1], and so on.
  3. It takes the first block of the first non-empty list. If that block is bigger than needed, expand halves it again and again: the lower half is kept, the upper half goes on the free list one order down.

From an empty zone, one page needs five splits: 32 → 16 + 16 free → 8 + 8 free → … → 1 + 1 free. The next 4-page request then finds a block on free_area[2] at once (Demo: split on allocation). Each step is O(1) list work, and there are at most MAX_ORDER steps.

Allocating one page from an empty 32-page zone: the order-5 block splits into two 16-page halves, the lower half splits into 8 + 8, then 4 + 4, 2 + 2 and 1 + 1; page 0 becomes allocation A and each upper half (16, 8, 4, 2 and 1 pages) stays on the free list of its order
Allocation halves the smallest big-enough block until it fits; every upper half goes on the free list one order down.

Freeing: merge with the buddy, repeat

__free_one_page computes the buddy of the freed block, buddy = pfn XOR 2k. If the buddy is a free block of the same order (the kernel marks such pages PageBuddy and stores the order in them), it is taken off its list and the two merge into a block of order k + 1 at the lower PFN; then the check repeats. Otherwise the block goes on free_area[k]. Because merging always happens as soon as possible, two free buddies of the same order never exist side by side.

In Demo: free and coalesce, freeing A (PFN 0) cannot merge because its buddy B (PFN 0 XOR 1 = 1) is in use. Freeing B merges it with A into an order-1 block, whose buddy (0 XOR 2 = 2) is C. Freeing C then merges 0–3, 0–7, 0–15 and 0–31: the zone is one block again.

Pages 0 to 31 with A at PFN 0, B at 1, C at 2–3 and free blocks 4–7, 8–15, 16–31. Freeing A: its buddy B is in use, no merge. Freeing B: merges with A into 0–1, whose buddy C is in use. Freeing C: merges into 0–3, 0–7, 0–15 and finally one free 32-page block
A freed block merges with its buddy whenever the buddy is free too, so the zone becomes one 32-page block again.

Internal and external fragmentation

Internal fragmentationExternal fragmentation
What is wastedpages inside an allocation that were not asked forfree pages that are not contiguous
Causerounding up to 2order: 5 pages → 8long-lived allocations scattered over memory
Demointernal fragmentation: 5, 9 and 3 pages use 8, 16 and 4: 11 of 32 pages wastedexternal fragmentation: 16 pages free, largest block 4 pages, an 8-page request fails
Remedyallocate exact sizes from a smaller allocator on top (slab), or free the tail (alloc_pages_exact)compaction; grouping pages by mobility

Compaction, and why unmovable pages matter

A page mapped into a process only through its page tables can be moved: allocate a new frame, copy the contents, point the page table entries at the new frame. Compaction (mm/compaction.c) runs two scanners: the migrate scanner walks up from the start of the zone looking for movable pages, the free scanner walks down from the end looking for free pages, and pages are moved from the first to the second until the scanners meet. The free pages collect at the start of the zone and merge into big blocks (Demo: compaction: A and C move up, and a 16-page request succeeds).

Kernel memory that is used through its physical or direct-mapped address (page tables, slab objects, most kernel buffers) is unmovable. One such page pins its whole region: in Demo: an unmovable page blocks compaction, C stays at PFN 8–11 and no 16-page block can form, although 16 pages are free. That is why Linux groups allocations by migratetype (movable, unmovable, reclaimable) into separate 2 MiB pageblocks, so unmovable pages do not end up scattered across memory. You can see the free counts per migratetype in /proc/pagetypeinfo.

How to see it on a real system

$ cat /proc/buddyinfo
Node 0, zone      DMA      0      0      0      0      0      0      0      0      1      1      3
Node 0, zone    DMA32   1850   1433    997    603    318    168     79     33     14      4    356
Node 0, zone   Normal  21468  12870   5329   1807    726    278    103     35     20     11   2413

Each column is nr_free of one order, from order 0 (4 KiB) to order 10 (4 MiB). Many small blocks and few large ones mean fragmented memory; echo 1 > /proc/sys/vm/compact_memory compacts all zones, and /proc/vmstat counts compaction attempts and failures (compact_stall, compact_fail).

What sits on top of the buddy allocator

  • Per-CPU page lists: single pages are freed to and allocated from a small per-CPU cache first, so most order-0 allocations take no lock.
  • The slab allocator (SLUB): kmalloc and object caches cut buddy pages into small objects (a 192-byte inode, a 64-byte buffer), which avoids the rounding waste for small sizes.
  • vmalloc: virtually contiguous memory made of scattered single pages, for large buffers that do not need physical contiguity.
  • Page faults: a process's pages come from here one at a time, see Virtual Memory.
  • malloc: user space never sees the buddy allocator directly; glibc gets pages with brk/mmap and cuts them into chunks, see Stack vs Heap in C.

What the page leaves out

Zones (DMA, DMA32, Normal) and NUMA nodes, each with its own free areas; the watermarks (min, low, high) that wake kswapd or make an allocation reclaim memory itself; the separate free lists per migratetype and the fallback between them; per-CPU lists; the GFP flags that say whether an allocation may sleep, do I/O or retry; and the OOM killer. Compaction here is simplified: it moves whole allocated blocks to the top end of the highest free block above them, instead of scanning single pages in pageblock steps.