ringbuffer даром
Оригинал https://t.me/bits_and_bicycles/25
Кольцевой буфер (он же ring buffer) – это структура данных #datastructure, которая позволяет тебе писать с одного конца (write
) и читать с другого (read
). Она классно подходит, когда тебе нужны FIFO очереди, потоки и всякие системы, где читатели с писателями работают отдельно друг от друга.
v read |kcheburek..............topke| ^ write
Но есть проблема. Ты лишаешься простоты последовательных блоков памяти, как при записи, так и при чтении. Теперь у тебя два куска памяти. Ну или всё таки один. Как получится, зависит от текущего положения внутри буфера. Приходится писать какие-то проверки, данные копировать в непрерывную память снаружи. Неудобно.
Ну и вроде ничего не поделаешь. Память ведь одним куском идёт.
Но доступ к памяти устроен чуть хитрее. Отдельный кусок процессора, MMU, транслирует адреса по табличке (для простоты давай считать, что блоками по 4KiB). И, что главное, разные адреса до трансляции могут вести в одно и тот же адрес в итоге. Работаешь ты с виртуальными адресами, а в итоге получешь адрес в физической памяти.
virtual mmu physical 0x00001??? 0x00002??? - ---\ /-->0x00002??? 0x00003??? \------>0x00003??? 0x00004??? - ------/ ...
Я думаю, ты уже понял куда я клоню. Ты можешь отобразить одни и те же 4KiB (или сколько там тебе надо) друг за другом. И получишь ring buffer даром! В #POSIX эта штука контроллируется, как и большинство настроек виртуальной памяти, через mmap. Физических адресов тебе, конечно, в user-space никто не даст, но они тебе и не нужны.
// Ищем место под 2 блока подряд в виртуальной памяти
base = mmap(0, 2 * size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
// Создаём файл в памяти (технический момент, полноценный файл нам не нужен)
int fd = memfd_create("/oh/my/ringbuffer", 0);
ftruncate(fd, size);
// Отображаем блоки в файл
mmap(base , size, PROT_xxx, MAP_FIXED | MAP_SHARED, fd, 0);
mmap(base + size, size, PROT_xxx, MAP_FIXED | MAP_SHARED, fd, 0);
read = write = base;
Всё, можешь читать свои 17 байт в read
или писать во write
. И не париться какая сейчас позиция в буфере: начало, середина, или вообще предпоследний байт.