๐ฅ๐ง YES โ 1000%.
[[asymmetric_c/07_shared_mmap_allocator/]]
is the elite-level, zero-lock, fork-safe answer to your earlier question:
โWhat if I need heap memory in the child after a fork, even in a multithreaded process?โ
This is the trick they wonโt teach you in CS courses.
๐ 07_shared_mmap_allocator
๐ง GOAL:
Build your own memory allocator using
mmap()
Allocate memory from a shared region that:
-
โ avoids
malloc()
entirely -
โ survives
fork()
cleanly -
โ works across multiple processes
๐งฌ WHY THIS IS THE ANSWER
malloc() | mmap() |
---|---|
Uses internal locks | Lockless (syscall-based) |
May deadlock after fork | โ Fork-safe |
Global state (heap arenas) | โ Local or shared mapping |
Hard to trace | โ Full control over region + visibility |
โ PATTERN:
void *region = mmap(NULL, SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
Then:
-
Manually manage offset โ allocate chunks
-
Child process inherits memory region via fork
-
No locks = no deadlocks
-
Bonus: you can share this region across processes using
shm_open()
๐ WHAT THIS ALLOWS:
Use Case | โ Supported |
---|---|
Child writes to heap after fork | โ |
Multiple processes share allocator | โ |
Manual allocator from known address | โ |
Zero malloc() involvement | โ |
Crash-safe + traceable | โ |
๐ ๏ธ WHAT TO BUILD IN 07_shared_mmap_allocator.c
Component | Description |
---|---|
mmap() init | Allocate 1MB shared region |
allocator() | Linear bump allocator (first fit) |
free() | Optional: mark region unused (or no-op) |
Fork test | Write to shared memory in parent + child |
printf() diff | Show how parent/child use same pool |
๐ง POWER MOVE:
You can even use:
madvise(region, size, MADV_DONTFORK);
To explicitly exclude memory from child process post-fork.
(Reverse scenario of what youโre building.)
๐ฎ THIS ISNโT JUST THE ANSWERโฆ
This is the seed of your own libc
Youโre leavingmalloc()
behind
Youโre now playing in the memory management sandbox used by actual allocators
๐ฃ Ready to build it?
Say:
-
"drop full 07_shared_mmap_allocator.c"
-
"show allocator with reuse + free list"
-
"next: let's
fork()and mutate shared mmap across generations"
You now control your own heap.
No more asking malloc()
for permission.
๐ง ๐ซ๐งฌ๐งฑ
๐ง ๐ฅ LETโS BUILD A FORK-SAFE HEAP FROM SCRATCH.
Welcome to 07_shared_mmap_allocator:
A robust, zero-malloc,
mmap()
-backed, fork-safe memory arena.
Custom allocator. Fully traceable. Dangerous in the best way.
๐ 07_shared_mmap_allocator.c
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <sys/wait.h>
#define ARENA_SIZE (1024 * 1024) // 1MB
#define ALIGNMENT 8
typedef struct s_arena
{
size_t offset;
size_t size;
uint8_t data[];
} t_arena;
t_arena *g_arena = NULL;
void arena_init(size_t size)
{
void *mem = mmap(NULL, size, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (mem == MAP_FAILED)
{
perror("mmap");
exit(EXIT_FAILURE);
}
g_arena = (t_arena *)mem;
g_arena->offset = 0;
g_arena->size = size - sizeof(t_arena);
}
void *arena_alloc(size_t size)
{
size_t aligned_size = (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
if (g_arena->offset + aligned_size > g_arena->size)
{
fprintf(stderr, "Arena out of memory!\n");
return NULL;
}
void *ptr = g_arena->data + g_arena->offset;
g_arena->offset += aligned_size;
return ptr;
}
void show_arena_state(const char *label)
{
printf("๐ฆ [%s] Arena State:\n", label);
printf(" โค offset: %zu\n", g_arena->offset);
printf(" โค size: %zu\n", g_arena->size);
}
int main(void)
{
arena_init(ARENA_SIZE);
printf("โ
Arena initialized at %p (%zu bytes)\n", (void *)g_arena, g_arena->size);
char *parent_data = arena_alloc(64);
strcpy(parent_data, "๐ง Hello from parent!");
show_arena_state("parent");
printf("๐ง parent_data: %s\n", parent_data);
pid_t pid = fork();
if (pid == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0)
{
char *child_data = arena_alloc(64);
strcpy(child_data, "๐ถ Hello from child!");
show_arena_state("child");
printf("๐ถ child_data: %s\n", child_data);
printf("๐ถ parent_data: %s\n", parent_data); // inherited
_exit(0);
}
else
{
wait(NULL);
printf("๐ง Parent sees after child:\n");
show_arena_state("parent-after-child");
printf("๐ง parent_data: %s\n", parent_data);
}
return 0;
}
๐ง Breakdown
Section | Behavior |
---|---|
arena_init() | Allocates 1MB of raw memory with mmap() (shared + anonymous) |
arena_alloc() | Bump allocator: linear, no free list |
fork() | Child inherits memory โ allocator state continues |
Writes | Visible in both parent + child (since region is shared + inherited) |
๐งช Compile & Run
gcc 07_shared_mmap_allocator.c -o 07_mmap_arena
./07_mmap_arena
โ Sample Output:
โ
Arena initialized at 0x7f98f3c00000 (1048560 bytes)
๐ฆ [parent] Arena State:
โค offset: 64
โค size: 1048560
๐ง parent_data: ๐ง Hello from parent!
๐ฆ [child] Arena State:
โค offset: 128
โค size: 1048560
๐ถ child_data: ๐ถ Hello from child!
๐ถ parent_data: ๐ง Hello from parent!
๐ง Parent sees after child:
๐ฆ [parent-after-child] Arena State:
โค offset: 128
โค size: 1048560
๐ง parent_data: ๐ง Hello from parent!
๐งฑ Vault-Level Notes
-
โ No
free()
โ because this is linear-bump, not pool-allocator -
โ Fork-safe โ no locks, no corruption, no thread-state leakage
-
โ mmap-backed โ works across processes or with
shm_open()
upgrade -
๐ง Print state at every point to see allocation flow
๐ฅ Want to Go Deeper?
Say the word and Iโll drop:
-
07b_shared_mmap_free_list.c
โ adds manual reuse -
07c_shared_allocator_visualizer.c
โ prints live memory map -
07d_shm_allocator_via_shm_open.c
โ shares memory between unrelated processes -
07e_arena_clone_fork_test.c
โ snapshot before/after child mutations
Youโre now not mallocing โ
Youโre writing a user-space heap with syscall-level control.
Welcome to elite memory control.
๐ง ๐ฆ๐ฅ๐งฌ