Created
November 17, 2021 07:58
-
-
Save lemnik/874d313aeef37017961ae8e0c5124669 to your computer and use it in GitHub Desktop.
mmap with some housekeeping
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <unistd.h> | |
#include <sys/mman.h> | |
// MEMORY_SANITIZER checks shamelessly stolen from Breakpad: src/common/memory_allocator.h | |
#if defined(MEMORY_SANITIZER) | |
#include <sanitizer/msan_interface.h> | |
#endif | |
#include "sigalloc.h" | |
/* | |
* We allocate at minimum 1 page at a time, and every allocation is aligned to a page. We call each group of pages | |
* allocated together a "chunk". | |
*/ | |
typedef struct { | |
size_t page_count; | |
void *next_chunk; | |
} chunk_header; | |
static void *allocate_pages(struct sig_allocator *allocator, size_t page_count) { | |
void *new_chunk = mmap(NULL, allocator->page_size * page_count, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
if(new_chunk == MAP_FAILED) { | |
return NULL; | |
} | |
#if defined(MEMORY_SANITIZER) | |
// We need to indicate to MSan that memory allocated through sys_mmap is initialized | |
__msan_unpoison(new_pages, allocator->page_size * page_count); | |
#endif | |
chunk_header *new_page_header = new_chunk; | |
new_page_header->next_chunk = allocator->current_chunk; | |
new_page_header->page_count = page_count; | |
allocator->page_count += page_count; | |
allocator->current_chunk = new_chunk; | |
return new_chunk; | |
} | |
void *sig_alloc(struct sig_allocator *allocator, const size_t size) { | |
if(!size) { | |
return NULL; | |
} | |
if(allocator->current_chunk) { | |
chunk_header *current_page_header = allocator->current_chunk; | |
// do we have space in the current page group? | |
if(allocator->next_alloc_offset + size + sizeof(chunk_header) < current_page_header->page_count * allocator->page_size) { | |
void *ptr = allocator->current_chunk + sizeof(chunk_header) + allocator->next_alloc_offset; | |
allocator->next_alloc_offset += size; | |
return ptr; | |
} | |
} | |
// we either don't have a valid page, or we don't have space in the current page group - so we ask for more | |
const size_t requested_page_count = (size + sizeof(chunk_header) + allocator->page_size - 1) / allocator->page_size; | |
void *new_pages = allocate_pages(allocator, requested_page_count); | |
if(!new_pages) { | |
return NULL; | |
} | |
allocator->next_alloc_offset = size; | |
return new_pages + sizeof(chunk_header); | |
} | |
void init_sig_allocator(struct sig_allocator *allocator) { | |
allocator->current_chunk = NULL; | |
allocator->page_count = 0; | |
allocator->page_size = sysconf(_SC_PAGESIZE); | |
allocator->next_alloc_offset = 0; | |
} | |
void destroy_sig_allocator(struct sig_allocator *allocator) { | |
void *current_chunk = allocator->current_chunk; | |
while(current_chunk) { | |
chunk_header *header = current_chunk; | |
// grab the next page *before* we unmap | |
current_chunk = header->next_chunk; | |
munmap(header, header->page_count * allocator->page_size); | |
} | |
allocator->current_chunk = NULL; | |
allocator->page_count = 0; | |
} | |
int sig_alocator_owns(struct sig_allocator *allocator, void *ptr) { | |
void *chunk = allocator->current_chunk; | |
while(chunk) { | |
chunk_header *header = chunk; | |
void *base = chunk + sizeof(chunk_header); | |
if(ptr > base && ptr < base + (header->page_count * allocator->page_size)) { | |
return 1; // this pointer is on one of our pages! | |
} | |
chunk = header->next_chunk; | |
} | |
return 0; // looked at all our pages, and couldn't find the pointer - must be someone elses | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef SIGALLOC_ALLOCATOR_H | |
#define SIGALLOC_ALLOCATOR_H | |
#include <stddef.h> | |
/** | |
* A signal-safe allocator structure. None of these fields should be used directly. | |
* | |
* Having the allocators as a struct instead of as globals allows several allocators to be "active" in a single process. | |
* In keeping with the "signal safe" theme, this allows safe allocation when there are "signals on top of signals", or multiple signals being handled | |
* concurrently by multiple threads. | |
*/ | |
struct sig_allocator { | |
/** | |
* The current chunk used by this allocator. This points to the header of the chunk and should not be used directly. | |
*/ | |
void *current_chunk; | |
/** | |
* The number of kernel-sized pages currently used by this allocator. | |
*/ | |
size_t page_count; | |
/** | |
* The size of the pages requested by this allocator. This defaults to the same size as the kernel, and should | |
* not be changed by externnal code. | |
*/ | |
int page_size; | |
/** | |
* The offset of the next allocation within the *usable* page. This does *not* take the page header into consideration | |
* and so cannot be directly used externally. | |
*/ | |
size_t next_alloc_offset; | |
}; | |
/** | |
* Similar to `malloc` but signal-safe and for a specific `sig_allocator`. | |
*/ | |
void *sig_alloc(struct sig_allocator *allocator, const size_t size); | |
/** | |
* Initialize the given sig_allocator. Memory pages are only requested when the first call to [sig_alloc] is made. | |
*/ | |
void init_sig_allocator(struct sig_allocator *allocator); | |
/** | |
* Destroy a given sig_allocator *and all* of it's associated data. This has the same effect as `free`ing every pointer | |
* allocated by the given allocator, and using any of those pointers further will likely result in a segmentation fault (SIGSEGV). | |
*/ | |
void destroy_sig_allocator(struct sig_allocator *allocator); | |
/** | |
* Returns non-zero if the given pointer is owned by the given sig_allocator. If true: destroying the allocator will invalidate the given pointer. | |
*/ | |
int sig_alocator_owns(struct sig_allocator *allocator, void *ptr); | |
#endif // SIGALLOC_ALLOCATOR_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment