#define _GNU_SOURCE
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// An "arena" for experimenting with MAP_FIXED mappings with overcommit
// disabled
struct arena {
void *base;
size_t reserved;
size_t committed;
};
struct arena a_new(size_t reserve_len) {
struct arena a = {0, reserve_len, 0};
a.base = mmap(NULL, reserve_len, PROT_NONE, MAP_PRIVATE
| MAP_ANONYMOUS, -1, 0);
if (a.base == MAP_FAILED) {
perror("vm reserve");
exit(1);
}
return a;
}
void *a_alloc(struct arena *a, size_t size) {
if (a->committed + size >= a->reserved
|| a->committed + size < a->committed) {
return NULL;
}
void *res = mmap(a->base + a->committed, size, PROT_READ | PROT_WRITE,
MAP_POPULATE | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
-1, 0);
if (res == MAP_FAILED) {
perror("expand map");
return NULL;
}
if (res != a->base + a->committed) {
fprintf(stderr, "new map is at the wrong location\n");
exit(1);
}
a->committed += size;
return res;
}
void a_dealloc(struct arena *a, size_t size) {
// Place a new PROT_NONE mapping over the end of the committed range
void *res = mmap(a->base + a->committed - size, size, PROT_NONE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
if (res == MAP_FAILED) {
perror("decommit");
exit(1);
}
if (res != a->base + a->committed - size) {
fprintf(stderr, "new map is in wrong place\n");
exit(1);
}
a->committed -= size;
}
void a_del(struct arena a) {
munmap(a.base, a.reserved);
}
int main(int argc, char *argv[]) {
const size_t gig = 1024 * 1024 * 1024;
puts("reserving virtual memory range");
struct arena a = a_new((size_t)128 * gig);
sleep(10);
puts("allocating memory");
a_alloc(&a, gig);
a_alloc(&a, gig);
a_alloc(&a, gig);
a_alloc(&a, gig);
a_alloc(&a, gig);
a_alloc(&a, gig);
a_alloc(&a, gig);
a_alloc(&a, gig);
puts("done");
sleep(10);
puts("deallocating");
a_dealloc(&a, 4 * gig);
sleep(5);
puts("reallocating");
a_alloc(&a, 4 * gig);
sleep(5);
a_del(a);
return 0;
}