#define _GNU_SOURCE #include #include #include #include #include // 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; }