<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rajat Negi</title>
    <description>The latest articles on DEV Community by Rajat Negi (@rn-ops).</description>
    <link>https://dev.to/rn-ops</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3909165%2Ffc54b19c-d1ec-45e0-85b1-62544b683e5d.png</url>
      <title>DEV Community: Rajat Negi</title>
      <link>https://dev.to/rn-ops</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rn-ops"/>
    <language>en</language>
    <item>
      <title>Building malloc from Scratch (Part 2): Bridging Linux, Windows, and Raw CPU Pages</title>
      <dc:creator>Rajat Negi</dc:creator>
      <pubDate>Wed, 08 Jul 2026 19:38:43 +0000</pubDate>
      <link>https://dev.to/rn-ops/building-malloc-from-scratch-part-2-bridging-linux-windows-and-raw-cpu-pages-4hb8</link>
      <guid>https://dev.to/rn-ops/building-malloc-from-scratch-part-2-bridging-linux-windows-and-raw-cpu-pages-4hb8</guid>
      <description>&lt;h2&gt;
  
  
  Recapping Part 1
&lt;/h2&gt;

&lt;p&gt;In Part 1, I focused on the design rather than the implementation. The allocator's scope, architecture, and core concepts were established before writing any code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  
  &lt;ul&gt;
&lt;li&gt;Cross-platform (Linux and Windows)&lt;/li&gt;
&lt;li&gt;Page-backed&lt;/li&gt;
&lt;li&gt;Explicit free-list&lt;/li&gt;
&lt;li&gt;Written in C
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Project Structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;memory-allocator/
├── include/
│   └── allocator.h
├── src/
│   ├── allocator.c
│   ├── block.c
│   ├── free_list.c
│   ├── utils.c
│   └── os/
│       ├── os.h
│       ├── linux.c
│       └── windows.c
└── tests/
    ├── Makefile
    └── test_os.c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Step 1: Defining the OS Interface&lt;/li&gt;
&lt;li&gt;
Step 2: Building the Platform Backends

&lt;ul&gt;
&lt;li&gt;Linux Backend&lt;/li&gt;
&lt;li&gt;Windows Backend&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Step 3: Utility Functions&lt;/li&gt;
&lt;li&gt;Step 4: Testing the Platform Layer&lt;/li&gt;
&lt;li&gt;What's Next&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 1: Defining the OS Interface
&lt;/h2&gt;

&lt;p&gt;The allocator itself should never know whether it is running on Linux or Windows.&lt;/p&gt;

&lt;p&gt;Instead, it interacts with a very small platform abstraction layer. Every request for raw memory goes through this interface, allowing the rest of the allocator to remain completely platform-independent.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/os/os.h&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#ifndef OS_H
#define OS_H
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stddef.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* Allocate memory directly from the operating system */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;os_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Release memory back to the operating system */&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;os_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cm"&gt;/* Query the system page size */&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;os_page_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These three functions form the boundary between the allocator and the operating system.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;os_alloc()&lt;/code&gt; requests one or more pages.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;os_free()&lt;/code&gt; releases previously allocated pages.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;os_page_size()&lt;/code&gt; retrieves the system's page size so the allocator never relies on hardcoded values such as &lt;code&gt;4096&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything built later like block headers, free lists, splitting, coalescing, &lt;code&gt;malloc()&lt;/code&gt;, &lt;code&gt;calloc()&lt;/code&gt;, &lt;code&gt;realloc()&lt;/code&gt;, and &lt;code&gt;free()&lt;/code&gt; will depend on these functions rather than directly calling operating system APIs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2: Building the Platform Backends
&lt;/h2&gt;

&lt;p&gt;The interface is identical across platforms.&lt;/p&gt;

&lt;p&gt;Only the implementation changes.&lt;/p&gt;

&lt;p&gt;On Linux, pages come from &lt;code&gt;mmap()&lt;/code&gt;. On Windows, they come from &lt;code&gt;VirtualAlloc()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Both implementations expose the same interface, allowing the allocator logic to remain unchanged regardless of the operating system.&lt;/p&gt;




&lt;h3&gt;
  
  
  Linux Backend
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/os/linux.c&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#ifdef __linux__
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"os.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;sys/mman.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;unistd.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;os_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PROT_READ&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;PROT_WRITE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MAP_PRIVATE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;MAP_ANONYMOUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;MAP_FAILED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;os_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;munmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;perror&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"munmap"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;os_page_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;page_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sysconf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_SC_PAGESIZE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page_size&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;4096&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;page_size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rather than using the traditional process heap (&lt;code&gt;sbrk()&lt;/code&gt;), this allocator requests independent virtual memory mappings using &lt;code&gt;mmap()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The call is configured with four important arguments:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PROT_READ | PROT_WRITE&lt;/code&gt; gives the mapping read and write permissions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MAP_PRIVATE&lt;/code&gt; ensures modifications remain private to the current process.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MAP_ANONYMOUS&lt;/code&gt; creates memory that is not backed by a file.&lt;/li&gt;
&lt;li&gt;Passing &lt;code&gt;NULL&lt;/code&gt; allows the kernel to choose an appropriate virtual address.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;On success, &lt;code&gt;mmap()&lt;/code&gt; returns a page-aligned, zero-initialized region of virtual memory.&lt;/p&gt;

&lt;p&gt;If the mapping cannot be created, it returns &lt;code&gt;MAP_FAILED&lt;/code&gt;. Rather than exposing Linux-specific error values to the allocator, the wrapper simply returns &lt;code&gt;NULL&lt;/code&gt;, providing the same behavior expected from a typical allocation function.&lt;/p&gt;

&lt;p&gt;Releasing memory is equally straightforward.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;munmap()&lt;/code&gt; returns the mapped pages to the operating system. Unlike Windows, Linux requires the original allocation size when unmapping a region, which is why &lt;code&gt;os_free()&lt;/code&gt; accepts both the pointer and the size.&lt;/p&gt;

&lt;p&gt;Finally, &lt;code&gt;os_page_size()&lt;/code&gt; queries the operating system using &lt;code&gt;sysconf()&lt;/code&gt; instead of assuming a fixed page size. While &lt;code&gt;4096&lt;/code&gt; bytes is common on modern desktop systems, other architectures may use different page sizes.&lt;/p&gt;




&lt;h3&gt;
  
  
  Windows Backend
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;src/os/windows.c&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#ifdef _WIN32
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"os.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;windows.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;os_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;VirtualAlloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MEM_RESERVE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;MEM_COMMIT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PAGE_READWRITE&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;os_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;VirtualFree&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;MEM_RELEASE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;os_page_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;SYSTEM_INFO&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;GetSystemInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dwPageSize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cp"&gt;#endif
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Windows exposes virtual memory management through the &lt;code&gt;VirtualAlloc()&lt;/code&gt; and &lt;code&gt;VirtualFree()&lt;/code&gt; APIs.&lt;/p&gt;

&lt;p&gt;Although the API differs from Linux, the overall idea is the same: request one or more pages directly from the operating system.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;VirtualAlloc()&lt;/code&gt; combines two operations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MEM_RESERVE&lt;/code&gt; reserves a range of virtual addresses.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MEM_COMMIT&lt;/code&gt; makes those pages immediately available for use.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using both flags together gives the allocator writable, zero-initialized memory in a single call.&lt;/p&gt;

&lt;p&gt;Unlike Linux, &lt;code&gt;VirtualFree()&lt;/code&gt; does &lt;strong&gt;not&lt;/strong&gt; require the allocation size when releasing memory. Windows keeps track of that information internally, so the wrapper simply ignores the unused &lt;code&gt;size&lt;/code&gt; parameter while preserving the same cross-platform interface.&lt;/p&gt;

&lt;p&gt;Retrieving the page size is handled through &lt;code&gt;GetSystemInfo()&lt;/code&gt;, which fills a &lt;code&gt;SYSTEM_INFO&lt;/code&gt; structure containing information about the running system, including the page size.&lt;/p&gt;




&lt;p&gt;Although the Linux and Windows implementations use completely different operating system APIs, they expose the same three functions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;os_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;os_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;os_page_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From this point onward, the allocator no longer needs to care which operating system it is running on.&lt;/p&gt;

&lt;p&gt;The platform-specific work ends here.&lt;/p&gt;

&lt;p&gt;Everything that follows like block headers, free lists, splitting, coalescing, and the implementation of &lt;code&gt;malloc()&lt;/code&gt; itself will be written entirely against this abstraction layer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3: Utility Functions
&lt;/h2&gt;

&lt;p&gt;With the platform layer complete, the next step is building a few small helper functions that the allocator will rely on throughout the project.&lt;/p&gt;

&lt;p&gt;These functions are not responsible for allocating memory. Instead, they provide common operations such as alignment, size comparisons, and block manipulation that will become useful once block headers and free lists are introduced.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;src/utils.c&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"allocator.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdbool.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stddef.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdint.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cm"&gt;/* Alignment utilities */&lt;/span&gt;
&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;align_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;alignment&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alignment&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;is_power_of_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;max_size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cm"&gt;/* Placeholder until BlockHeader exists */&lt;/span&gt;
&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;blocks_are_adjacent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;block1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;block2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;block1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;block2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although this file is small, it introduces one of the most important ideas in allocator design: memory alignment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alignment
&lt;/h2&gt;

&lt;p&gt;Modern processors expect many types of data to begin at addresses that are multiples of a specific value, typically 8 or 16 bytes on 64-bit systems.&lt;/p&gt;

&lt;p&gt;Suppose a program requests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;malloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returning exactly three bytes would leave the next allocation starting at an unaligned address.&lt;/p&gt;

&lt;p&gt;Instead, allocators round allocations upward so every payload begins on the correct alignment boundary.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;malloc(3)
      │
      ▼
Round up to 8 bytes
      │
      ▼
+--------+-----------+
| Header |  Payload  |
+--------+-----------+
      │
      ▼
Next allocation also begins on an aligned boundary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Proper alignment improves performance and is required by some processor architectures.&lt;/p&gt;

&lt;p&gt;The helper responsible for this is &lt;code&gt;align_up()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="nf"&gt;align_up&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;alignment&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;alignment&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alignment&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although the expression looks unusual, it performs two simple operations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add enough bytes to reach the next alignment boundary.&lt;/li&gt;
&lt;li&gt;Clear the lower bits to round the value down to that boundary.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;align_up(5, 8)  = 8
align_up(8, 8)  = 8
align_up(9, 8)  = 16
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This works only when the alignment is a power of two.&lt;/p&gt;

&lt;p&gt;To verify that assumption, the allocator includes another helper.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="nf"&gt;is_power_of_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This relies on a common bit manipulation trick.&lt;/p&gt;

&lt;p&gt;A power of two contains exactly one bit set.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8  = 1000
7  = 0111

1000 &amp;amp;
0111
----
0000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the result is zero, the value is a power of two.&lt;/p&gt;

&lt;p&gt;The remaining helpers are much simpler.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;max_size()&lt;/code&gt; returns the larger of two values and improves readability wherever block sizes need to be compared.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;blocks_are_adjacent()&lt;/code&gt; is currently only a placeholder. Once block headers are introduced, it will determine whether two neighboring blocks can be merged during coalescing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4: Testing the Platform Layer
&lt;/h2&gt;

&lt;p&gt;Before building the allocator itself, it is worth verifying that the platform abstraction layer behaves correctly.&lt;/p&gt;

&lt;p&gt;At this stage, the goal is intentionally simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Query the system page size.&lt;/li&gt;
&lt;li&gt;Request one page from the operating system.&lt;/li&gt;
&lt;li&gt;Write data into that page.&lt;/li&gt;
&lt;li&gt;Read it back.&lt;/li&gt;
&lt;li&gt;Release the page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this works on both Linux and Windows, the allocator has a reliable foundation to build upon.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tests/test_os.c&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;stdio.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
#include&lt;/span&gt; &lt;span class="cpf"&gt;&amp;lt;string.h&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="cp"&gt;#include&lt;/span&gt; &lt;span class="cpf"&gt;"os/os.h"&lt;/span&gt;&lt;span class="cp"&gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Testing OS abstraction layer...&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;page_size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os_page_size&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Page size: %zu bytes&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page_size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page_size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fprintf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Allocation failed!&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Allocated %zu bytes at %p&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Hello, Memory Allocator!"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="n"&gt;strcpy&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Read back: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;%s&lt;/span&gt;&lt;span class="se"&gt;\"\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;os_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;page_size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;printf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Memory released successfully.&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The test is intentionally small.&lt;/p&gt;

&lt;p&gt;No allocator has been implemented yet.&lt;/p&gt;

&lt;p&gt;The only purpose is to verify that both platform backends expose identical behavior through the abstraction layer.&lt;/p&gt;

&lt;p&gt;To simplify compilation, the project uses a small Makefile.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;tests/Makefile&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;CC&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; gcc

&lt;span class="nv"&gt;CFLAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-Wall&lt;/span&gt; &lt;span class="nt"&gt;-Wextra&lt;/span&gt; &lt;span class="nt"&gt;-std&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;c11 &lt;span class="nt"&gt;-I&lt;/span&gt;../include &lt;span class="nt"&gt;-I&lt;/span&gt;../src

&lt;span class="nv"&gt;LDFLAGS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

&lt;span class="nv"&gt;UNAME_S&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;shell &lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;ifeq&lt;/span&gt; &lt;span class="nv"&gt;($(UNAME_S),Linux)&lt;/span&gt;
  &lt;span class="nv"&gt;OS_BACKEND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ../src/os/linux.c
&lt;span class="k"&gt;endif&lt;/span&gt;

&lt;span class="k"&gt;ifeq&lt;/span&gt; &lt;span class="nv"&gt;($(UNAME_S),Darwin)&lt;/span&gt;
  &lt;span class="nv"&gt;OS_BACKEND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ../src/os/linux.c
&lt;span class="k"&gt;endif&lt;/span&gt;

&lt;span class="k"&gt;ifeq&lt;/span&gt; &lt;span class="nv"&gt;($(OS),Windows_NT)&lt;/span&gt;
  &lt;span class="nv"&gt;OS_BACKEND&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; ../src/os/windows.c
&lt;span class="k"&gt;endif&lt;/span&gt;

&lt;span class="nl"&gt;all&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;test_os&lt;/span&gt;

&lt;span class="nl"&gt;test_os&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;test_os.c $(OS_BACKEND) ../src/utils.c&lt;/span&gt;
  &lt;span class="err"&gt;$(CC)&lt;/span&gt; &lt;span class="err"&gt;$(CFLAGS)&lt;/span&gt; &lt;span class="err"&gt;-o&lt;/span&gt; &lt;span class="err"&gt;$@&lt;/span&gt; &lt;span class="err"&gt;test_os.c&lt;/span&gt; &lt;span class="err"&gt;$(OS_BACKEND)&lt;/span&gt; &lt;span class="err"&gt;../src/utils.c&lt;/span&gt; &lt;span class="err"&gt;$(LDFLAGS)&lt;/span&gt;

&lt;span class="nl"&gt;clean&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
  &lt;span class="err"&gt;rm&lt;/span&gt; &lt;span class="err"&gt;-f&lt;/span&gt; &lt;span class="err"&gt;test_os&lt;/span&gt;

&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;all clean&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Makefile selects the appropriate platform backend automatically.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Linux builds with &lt;code&gt;linux.c&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Windows builds with &lt;code&gt;windows.c&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;macOS currently reuses the Linux backend because it also provides &lt;code&gt;mmap()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once built, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;make
./test_os
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A successful run should produce output similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Testing OS abstraction layer...
Page size: 4096 bytes
Allocated 4096 bytes at 0x71002a37c000
Read back: "Hello, Memory Allocator!"
Memory released successfully.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At this point, the project can successfully communicate with the operating system, request pages, access them, and return them safely.&lt;/p&gt;

&lt;p&gt;That is all the allocator needs before introducing its own memory management.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;The platform abstraction layer is now complete.&lt;/p&gt;

&lt;p&gt;The allocator can request pages from the operating system, determine the system page size, and release memory through a common interface that works on both Linux and Windows.&lt;/p&gt;

&lt;p&gt;In the next part, those raw pages will become usable memory blocks.&lt;/p&gt;

&lt;p&gt;We'll introduce block headers, define the allocator's internal metadata, and begin implementing the data structures that eventually power &lt;code&gt;malloc()&lt;/code&gt;, &lt;code&gt;calloc()&lt;/code&gt;, &lt;code&gt;realloc()&lt;/code&gt;, and &lt;code&gt;free()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rn-ops/memory-allocator-in-c" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Explore the project on GitHub&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>c</category>
      <category>linux</category>
      <category>memoryallocator</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Building malloc from Scratch (Part 1): Architecture &amp; Core Concepts</title>
      <dc:creator>Rajat Negi</dc:creator>
      <pubDate>Wed, 08 Jul 2026 15:52:32 +0000</pubDate>
      <link>https://dev.to/rn-ops/building-malloc-from-scratch-part-1-architecture-core-concepts-3mfm</link>
      <guid>https://dev.to/rn-ops/building-malloc-from-scratch-part-1-architecture-core-concepts-3mfm</guid>
      <description>&lt;p&gt;I wanted to understand how &lt;code&gt;malloc&lt;/code&gt; actually works under the hood.&lt;/p&gt;

&lt;p&gt;Most explanations I found online described &lt;em&gt;what&lt;/em&gt; an allocator does, but completely skipped over the "why" behind its design decisions. Rather than stopping at theory, I decided to build a cross-platform allocator in C that implements &lt;code&gt;malloc&lt;/code&gt;, &lt;code&gt;calloc&lt;/code&gt;, &lt;code&gt;realloc&lt;/code&gt;, and &lt;code&gt;free&lt;/code&gt; from scratch.&lt;/p&gt;

&lt;p&gt;This article documents the design of that allocator, the architectural tradeoffs I faced, and the core concepts I had to learn along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table Of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Design Decisions&lt;/li&gt;
&lt;li&gt;Architecture&lt;/li&gt;
&lt;li&gt;Modern Allocator Strategies&lt;/li&gt;
&lt;li&gt;Core Concepts&lt;/li&gt;
&lt;li&gt;What's Next&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Project Overview
&lt;/h2&gt;

&lt;p&gt;A custom memory allocator does not create physical memory. Instead, it requests pages of raw virtual memory from the operating system and manages how that memory is partitioned, reused, resized, and released by the application.&lt;/p&gt;

&lt;p&gt;The goal of this educational project is to implement C's core memory management API using a modular, cross-platform architecture inspired by design principles found in modern allocators, rather than relying on legacy, single-platform tricks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Design Decisions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  #1: Why I am Skipping &lt;code&gt;sbrk&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;While many classic tutorials use &lt;code&gt;sbrk&lt;/code&gt; for educational implementations, I deliberately chose a &lt;strong&gt;100% &lt;code&gt;mmap&lt;/code&gt;-based approach&lt;/strong&gt; for two major reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;sbrk&lt;/code&gt; is a fragile global bottleneck.&lt;/strong&gt; It works by moving a single pointer (the program break) up and down. This means the allocator assumes it owns a contiguous line of memory. If a third-party library or another thread in the program secretly calls &lt;code&gt;sbrk&lt;/code&gt; behind the scenes, the allocator's memory layout can break instantly. &lt;code&gt;mmap&lt;/code&gt;, by contrast, provides isolated, independent chunks of memory.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-Platform Symmetry.&lt;/strong&gt; Windows has absolutely no equivalent to &lt;code&gt;sbrk&lt;/code&gt;, but it has a direct equivalent to &lt;code&gt;mmap&lt;/code&gt;: &lt;code&gt;VirtualAlloc&lt;/code&gt;. If we used &lt;code&gt;sbrk&lt;/code&gt;, our architectural abstraction (&lt;code&gt;os_alloc&lt;/code&gt;) would become awkward because Linux would deal with a moving pointer while Windows dealt with independent pages. Using &lt;code&gt;mmap&lt;/code&gt; keeps the abstraction perfectly symmetrical: we ask for $N$ pages of memory, and the OS gives us a pointer.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  #2: Scope
&lt;/h3&gt;

&lt;p&gt;This project implements a &lt;strong&gt;cross-platform, page-backed explicit free-list&lt;/strong&gt; memory allocator in C.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform&lt;/strong&gt; because the allocator separates platform-specific memory acquisition (mmap on Linux and VirtualAlloc on Windows) from the allocator logic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page-backed&lt;/strong&gt; because it requests memory from the operating system in page-sized chunks instead of relying on the legacy sbrk interface.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explicit free-list&lt;/strong&gt; because free memory blocks are tracked using an explicit linked list, allowing blocks to be reused, split, and coalesced over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Modular&lt;/strong&gt; because the allocator logic is isolated from platform-specific memory acquisition through a small OS abstraction layer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is &lt;code&gt;not an arena allocator, a toy bump allocator, slab allocator, or production-grade allocator&lt;/code&gt;. The goal is to build a clean, modular allocator that demonstrates the fundamental algorithms and data structures behind dynamic memory management while remaining portable across Linux and Windows.&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture and Platform Abstraction
&lt;/h2&gt;

&lt;p&gt;To keep the codebase clean, we decouple the high-level allocation logic (which is identical across platforms) from the low-level OS system calls. The only platform-specific layer is responsible for fetching and releasing large chunks of virtual memory.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mental Model to Keep
&lt;/h3&gt;


&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Application
       │
[ malloc / calloc / realloc / free ]
       │
─────────────────────────────────
Allocator Logic
• free list
• splitting
• coalescing
• alignment
─────────────────────────────────
OS Abstraction
os_alloc()
os_free()
─────────────────────────────────
Linux             Windows
mmap()            VirtualAlloc()
munmap()          VirtualFree()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;/div&gt;



&lt;h3&gt;
  
  
  Project Directory Structure
&lt;/h3&gt;


&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;memory-allocator/
├── include/
│   └── allocator.h
├── src/
│   ├── allocator.c       # Implementations of malloc, calloc, realloc, free
│   ├── block.c           # Block header manipulation (splitting, coalescing)
│   ├── free_list.c       # Free list management (allocation strategies)
│   ├── utils.c           # Alignment helpers and math utilities
│   └── os/               # OS abstraction layer
│       ├── os.h
│       ├── linux.c       # Linux mmap/munmap backend
│       └── windows.c     # Windows VirtualAlloc/VirtualFree backend
└── tests/
    ├── Makefile
    └── test_os.c

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;/div&gt;



&lt;h3&gt;
  
  
  OS Abstraction Layer
&lt;/h3&gt;

&lt;p&gt;The entire platform-specific interface is reduced to just two primary functions declared in &lt;code&gt;os.h&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;/p&gt;
  See the os.h header file
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="nf"&gt;os_alloc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt;  &lt;span class="nf"&gt;os_free&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;ptr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Linux Backend:&lt;/strong&gt; Maps to &lt;code&gt;mmap&lt;/code&gt; and &lt;code&gt;munmap&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Windows Backend:&lt;/strong&gt; Maps to &lt;code&gt;VirtualAlloc&lt;/code&gt; and &lt;code&gt;VirtualFree&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By abstracting these calls, core features like &lt;strong&gt;block headers, free-list management, memory splitting, coalescing, and data alignment&lt;/strong&gt; remain completely platform-independent.&lt;/p&gt;




&lt;h2&gt;
  
  
  Modern Allocator Strategies
&lt;/h2&gt;

&lt;p&gt;Historically, general-purpose allocators relied heavily on brk/sbrk for heap growth and used mmap primarily for larger allocations.&lt;/p&gt;

&lt;p&gt;Modern production-grade allocators take a page-based or arena-based (extremely fast, used in games, compilers, parsers - where many objects have the same lifetime.) approach to minimize fragmentation and maximize multi-threaded performance:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Allocator&lt;/th&gt;
&lt;th&gt;Strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;glibc malloc&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Dynamically mixes &lt;code&gt;brk&lt;/code&gt; for small, short-lived objects and &lt;code&gt;mmap&lt;/code&gt; for larger allocations.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;jemalloc&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Heavily relies on &lt;code&gt;mmap&lt;/code&gt; to allocate large chunks ("extents"), optimizing for multi-threaded concurrency and core scaling.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;mimalloc&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uses page mapping (&lt;code&gt;mmap&lt;/code&gt;/&lt;code&gt;VirtualAlloc&lt;/code&gt;) to slice memory into uniform-sized blocks, prioritizing predictability and free-list security.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;tcmalloc&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Thread-Caching allocator that utilizes a centralized page heap manager to distribute thread-local memory structures rapidly.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Core Concept
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Virtual Memory
&lt;/h3&gt;

&lt;p&gt;Before an allocator can manage memory, it's important to understand what kind of memory it is actually managing.&lt;/p&gt;

&lt;p&gt;User programs do not manipulate physical RAM directly. Instead, every process is given its &lt;strong&gt;own virtual address space&lt;/strong&gt; by the operating system. The kernel maps these virtual addresses onto physical memory as needed, providing isolation between processes and allowing techniques such as demand paging, memory mapping, and copy-on-write.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Region&lt;/th&gt;
&lt;th&gt;Address Direction&lt;/th&gt;
&lt;th&gt;Access Rights&lt;/th&gt;
&lt;th&gt;Contents &amp;amp; Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Protected Kernel Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High to Low&lt;/td&gt;
&lt;td&gt;Kernel Only (No User Access)&lt;/td&gt;
&lt;td&gt;Operating system core, page tables, and drivers.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stack&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High to Low&lt;/td&gt;
&lt;td&gt;r-w&lt;/td&gt;
&lt;td&gt;Function arguments, local variables, and return addresses.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mapped Memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High to Low&lt;/td&gt;
&lt;td&gt;r-w-x (Depends)&lt;/td&gt;
&lt;td&gt;Memory-mapped files (&lt;code&gt;mmap&lt;/code&gt;), shared libraries, and thread-local storage.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Heap&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High to Low&lt;/td&gt;
&lt;td&gt;r-w&lt;/td&gt;
&lt;td&gt;Dynamically allocated memory (e.g., via &lt;code&gt;malloc&lt;/code&gt; in C or &lt;code&gt;new&lt;/code&gt; in C++).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Program Break (brk)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High to Low&lt;/td&gt;
&lt;td&gt;r-w&lt;/td&gt;
&lt;td&gt;The boundary/break marking the end of the data segment. It's not an actual region.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Text Segment&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low to High&lt;/td&gt;
&lt;td&gt;r-x&lt;/td&gt;
&lt;td&gt;The compiled machine code/instructions of the program.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Pages
&lt;/h3&gt;

&lt;p&gt;Virtual memory is divided into fixed-size pages (typically &lt;code&gt;4 KiB on x86-64 Linux&lt;/code&gt;). The operating system maps and protects memory one page at a time rather than one byte at a time.&lt;/p&gt;

&lt;p&gt;Consequently, functions like &lt;code&gt;mmap()&lt;/code&gt; and &lt;code&gt;VirtualAlloc()&lt;/code&gt; allocate memory in page-sized units. Our allocator will request pages from the operating system and then subdivide them into smaller blocks that satisfy individual &lt;code&gt;malloc()&lt;/code&gt; requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OS Level:     [    4 KiB Page    ] -&amp;gt; [    4 KiB Page    ]
                     ↓                      ↓
Allocator:    [Block][Block][Block]   [Block][Block][Block]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;blockquote&gt;
&lt;p&gt;This difference in granularity is the entire reason memory allocators exist. The OS manages the pages; our allocator manages the blocks within them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  3. Anonymous Mapping
&lt;/h3&gt;

&lt;p&gt;This allocator requests anonymous memory mappings rather than mapping files. An anonymous mapping is simply a region of virtual memory backed by the OS rather than a file on disk. Anonymous pages are zero-filled when first mapped by the kernel.&lt;/p&gt;

&lt;p&gt;Our OS abstraction layer will utilize this specific wrapper signature:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;mmap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;PROT_READ&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;PROT_WRITE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;MAP_PRIVATE&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;MAP_ANONYMOUS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;
I originally thought mmap() was only for mapping files. In reality, anonymous mappings are widely used by modern allocators to obtain large regions of virtual memory from the operating system.&lt;br&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. Block
&lt;/h3&gt;

&lt;p&gt;Applications rarely request memory in page-sized units. Instead, programs request arbitrary numbers of bytes like &lt;code&gt;malloc(24)&lt;/code&gt; or &lt;code&gt;malloc(137)&lt;/code&gt;, while the OS allocates memory in pages (4 KiB). The allocator bridges this difference by subdividing pages into smaller memory blocks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+-------------------------------------------------------------+
|   Header  |  Data Block  |  Header  |  Data Block  |  Free  |
+-------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  5. Metadata
&lt;/h3&gt;

&lt;p&gt;Raw pages from the OS contain no information about how they're being used. The allocator adds a small metadata header before each allocation so it can track block size, allocation state, and free-list links.&lt;/p&gt;

&lt;p&gt;One possible header layout is:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BlockHeader&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;free&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;BlockHeader&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;next&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┬───────────────────┬────────────────┐
│ Metadata Header │   User Payload    │ Padding/Align. │
│ (Size, Status)  │ (Returned Pointer)│ (Word Boundary)│
└─────────────────┴───────────────────┴────────────────┘
                  ▲
                  └─ Pointer returned to the application
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;/div&gt;



&lt;p&gt;A standard explicit free-list structure chains these blocks together using pointers embedded within the metadata or the free payload space:&lt;/p&gt;


&lt;div class="crayons-card c-embed"&gt;

  &lt;br&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Header | Next] ---&amp;gt; [Header | Next] ---&amp;gt; [Header | Next]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;/div&gt;



&lt;h3&gt;
  
  
  6. Tracking Free Memory
&lt;/h3&gt;

&lt;p&gt;Once blocks exist, the allocator needs a way to remember which ones are available for reuse. There are many ways to track free memory, but here are the two basic ones:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Method 1: Implicit free list&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[H][Used]
[H][Used]
[H][Free]
[H][Used]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Method 2: Explicit free list&lt;/strong&gt;&lt;br&gt;
Once blocks are freed by the user, we need a tracking mechanism. While an implicit list forces you to hop through every single block (used or free) to find an empty slot, our allocator will implement an Explicit Free List.&lt;/p&gt;

&lt;p&gt;By using the next pointer inside our metadata headers, we link only the available blocks together into a dedicated chain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Free List Head
      │
      ↓
+---------+     +---------+     +---------+
| Header  | --&amp;gt; | Header  | --&amp;gt; | Header  |
|  Free   |     |  Free   |     |  Free   |
+---------+     +---------+     +---------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a request comes in, we can jump sequentially through this list using search strategies like First-Fit (take the first block that fits) or Best-Fit (find the block closest in size) to find a home for the data.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Alignment
&lt;/h3&gt;

&lt;p&gt;Many architectures require or strongly prefer naturally aligned accesses. Proper alignment avoids undefined behavior on some systems and improves performance on many others. If a user requests malloc(3), we must round that payload size up to 8 bytes. Ignoring alignment leads to severe &lt;strong&gt;performance degradation&lt;/strong&gt; or &lt;strong&gt;outright crashes&lt;/strong&gt; on strict architectures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;malloc(3)
  ↓
Header
  ↓
Payload rounded to 8 bytes
  ↓
Next allocation begins on an aligned boundary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  8. Splitting
&lt;/h3&gt;

&lt;p&gt;If our free list finds a leftover block that is 256 bytes large, but the user only requested &lt;code&gt;malloc(64)&lt;/code&gt;, we shouldn't waste the remaining space. We split the block: carving out the requested space, stamping a new metadata header on the remaining 192 bytes, and throwing that remnant back into the free list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         +----------------------------------+
(Before) |          Free 256 bytes          |
         +----------------------------------+
                         ↓
          Need 64 bytes (malloc(64))
                         ↓
        +----------------+-----------------+
(After) | Head | 64 Used | Head | 192 free |
        +----------------+-----------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  9. Coalescing
&lt;/h3&gt;

&lt;p&gt;Splitting memory introduces a new issue: &lt;strong&gt;external fragmentation&lt;/strong&gt;. If we free a 64-byte block right next to a 192-byte free block, we technically have 256 bytes of contiguous space. However, if our allocator looks at them as two isolated items, a subsequent request for 200 bytes will fail.&lt;/p&gt;

&lt;p&gt;When a block is freed, the allocator checks whether the immediately adjacent blocks in memory are also free. If they are, it coalesces them, merging their boundaries into a single large bloc&lt;/p&gt;

&lt;p&gt;Basically this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;         +-----------+-----------+-----------+-----------+
(Before) | H  | Used | H  |Free  | H  |Free  | H  | Used |
         +-----------+-----------+-----------+-----------+
                                 ↓
         +-----------+-----------------------+-----------+
(After)  | H  | Used | H  | Large Free Block | H  | Used |
         +-----------+-----------------------+-----------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;The design is now in place.&lt;/p&gt;

&lt;p&gt;The allocator has a defined architecture, a platform abstraction layer, and a clear strategy for obtaining and managing memory. The next step is to replace diagrams with code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In Part 2&lt;/strong&gt;, I'll implement the operating system abstraction layer by building &lt;code&gt;os_alloc()&lt;/code&gt; and &lt;code&gt;os_free()&lt;/code&gt; for both Linux and Windows. Once page allocation is working, the allocator itself can begin taking shape—starting with block headers and progressively adding free-list management, splitting, coalescing, and the implementations of &lt;code&gt;malloc()&lt;/code&gt;, &lt;code&gt;calloc()&lt;/code&gt;, &lt;code&gt;realloc()&lt;/code&gt;, and &lt;code&gt;free()&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Every memory allocator starts with the same fundamental constraint: the operating system only provides pages of virtual memory. Everything beyond that, block metadata, free lists, alignment, splitting, coalescing, and the standard allocation functions - is an engineering problem built on top of those pages.&lt;/p&gt;

&lt;p&gt;This article establishes the design decisions and architecture that will guide the rest of the project. The implementation that follows will build each component incrementally until the allocator becomes a complete, cross-platform implementation of C's dynamic memory allocation API.&lt;/p&gt;

&lt;p&gt;If you're interested in following the project as it evolves, the full source code, documentation, and development history are available on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rn-ops/memory-allocator-in-c" class="crayons-btn crayons-btn--primary" rel="noopener noreferrer"&gt;Explore the project on GitHub&lt;/a&gt;
&lt;/p&gt;

</description>
      <category>c</category>
      <category>linux</category>
      <category>memoryallocator</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
