Last active
January 19, 2026 19:02
-
-
Save zeusdeux/1d13f1516c9c345195f92e8ffb971e2c to your computer and use it in GitHub Desktop.
mmap same underlying memory twice into a virtual address space of twice the underlying memory size for use as a circular buffer/ring buffer
This file contains hidden or 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 <errno.h> | |
| #include <stddef.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <sys/mman.h> | |
| #include <fcntl.h> | |
| #include <unistd.h> | |
| typedef enum { | |
| L_ERROR = 0, | |
| L_WARN, | |
| L_INFO, | |
| L_COUNT, | |
| } LOG_LEVEL; | |
| static const char *LOG_LEVEL_STR[L_COUNT] = { | |
| "ERROR", | |
| "WARN", | |
| "INFO" | |
| }; | |
| #define log(level, ...) do { \ | |
| fprintf(stderr, "%s:%d: ", __FILE__, __LINE__); \ | |
| fprintf(stderr, "[%s] ", LOG_LEVEL_STR[(level)]); \ | |
| fprintf(stderr, __VA_ARGS__); \ | |
| fprintf(stderr, "\n"); \ | |
| } while(0) | |
| #define bail(...) do { \ | |
| fprintf(stderr, "%s:%d:\t[%s] ", __FILE__, __LINE__, __func__); \ | |
| fprintf(stderr, __VA_ARGS__); \ | |
| fprintf(stderr, "\n"); \ | |
| exit(EXIT_FAILURE); \ | |
| } while(0) | |
| #define fuckoff(f) do { \ | |
| if (fclose((f)) < 0) { \ | |
| bail("Failed to close temp file due to %s", strerror(errno)); \ | |
| } \ | |
| } while(0) | |
| #define fuckoffmore(ptr, sz) do { \ | |
| if ((ptr) != NULL) { \ | |
| if (munmap((ptr), (sz)) < 0) { \ | |
| bail("Failed to munmap %p of size %zu bytes", (void *)(ptr), (sz)); \ | |
| } \ | |
| } \ | |
| } while(0) | |
| // gcc -std=c17 -Wall -Wextra -Wdeprecated -Wpedantic -pedantic -g -DDEBUG -o mmap-circular-buffer mmap-circular-buffer.c && ./mmap-circular-buffer | |
| int main(void) | |
| { | |
| long page_size = sysconf(_SC_PAGESIZE); | |
| FILE *tmp_file = tmpfile(); | |
| log(L_INFO, "System page size: %ld bytes", page_size); | |
| if (tmp_file == NULL) { | |
| fuckoff(tmp_file); | |
| bail("Failed to open temp file due to %s", strerror(errno)); | |
| } | |
| /* int fd = shm_open(circular_buffer_file_name, O_RDWR | O_CREAT | O_EXCL, 0666); // this shit doesn't work on macos */ | |
| int fd = fileno(tmp_file); | |
| size_t n = page_size / sizeof(size_t); | |
| size_t sz = n * sizeof(size_t); | |
| log(L_INFO, "Number of elements in circular buffer: %zu (%zu bytes)", n, sz); | |
| if (ftruncate(fd, sz) < 0) { | |
| fuckoff(tmp_file); | |
| bail("ftruncate failed on fd %d due to %s", fd, strerror(errno)); | |
| } | |
| log(L_INFO, "Truncated temp file to %zu bytes", sz); | |
| size_t circular_buffer_sz = sz * 2; | |
| size_t *circular_buffer = mmap(NULL, circular_buffer_sz, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); | |
| if (circular_buffer == MAP_FAILED) { | |
| fuckoff(tmp_file); | |
| fuckoffmore(circular_buffer, circular_buffer_sz); | |
| bail("mmap of circular buffer failed due to %s", strerror(errno)); | |
| } | |
| log(L_INFO, "Got %zu bytes of virtual address space starting at %p", circular_buffer_sz, (void *)circular_buffer); | |
| size_t region_sz = sz; | |
| size_t *first = mmap(circular_buffer, region_sz, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0); | |
| if (first == MAP_FAILED) { | |
| fuckoff(tmp_file); | |
| fuckoffmore(first, region_sz); | |
| fuckoffmore(circular_buffer, circular_buffer_sz); | |
| bail("mmap of first region of circular buffer failed due to %s", strerror(errno)); | |
| } | |
| log(L_INFO, "Mapped first half of circular buffer (starting = %p, size = %zu bytes) to backing temp file ", (void *)first, region_sz); | |
| size_t *second = mmap((void *)((ptrdiff_t)circular_buffer + region_sz), region_sz , PROT_READ | PROT_WRITE, MAP_FIXED | MAP_SHARED, fd, 0); | |
| if (second == MAP_FAILED) { | |
| fuckoff(tmp_file); | |
| fuckoffmore(first, region_sz); | |
| fuckoffmore(second, region_sz); | |
| fuckoffmore(circular_buffer, circular_buffer_sz); | |
| bail("mmap of second region of circular buffer failed due to %s", strerror(errno)); | |
| } | |
| log(L_INFO, "Mapped second half of circular buffer (starting = %p, size = %zu bytes) to the SAME backing temp file ", (void *)second, region_sz); | |
| log(L_INFO, "------------------------------------------------------------"); | |
| circular_buffer[0] = 123; | |
| log(L_INFO, "set circular_buffer[%4d]: %d", 0, 123); | |
| circular_buffer[2047] = 456; | |
| log(L_INFO, "set circular_buffer[%d]: %d", 2047, 456); | |
| log(L_INFO, "------------------------------------------------------------"); | |
| log(L_INFO, "get circular_buffer[%4d]: %zu", 0, circular_buffer[0]); | |
| log(L_INFO, "get circular_buffer[%d]: %zu", 2047, circular_buffer[2047]); | |
| log(L_INFO, "------------------------------------------------------------"); | |
| log(L_INFO, "Access beyond 2048 elements wraps around as shown below"); | |
| log(L_INFO, "get circular_buffer[%d]: %zu", 2048, circular_buffer[2048]); | |
| log(L_INFO, "get circular_buffer[%d]: %zu", 4095, circular_buffer[4095]); | |
| log(L_INFO, "------------------------------------------------------------"); | |
| fuckoff(tmp_file); | |
| fuckoffmore(first, region_sz); | |
| fuckoffmore(second, region_sz); | |
| fuckoffmore(circular_buffer, circular_buffer_sz); | |
| log(L_INFO, "Cleaned up all resources"); | |
| return 0; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment