Одной из опций является использование ввода / вывода сбора / разброса с recvmsg/sendmsg
, где вы можете предоставить несколько областей памяти для чтения / записи из / в.Для кольцевого буфера вы бы предоставили 2 области, где вторая область - это обернутая часть.
Хотя это и не идеально, потому что ваш кольцевой буфер все еще не является смежным и когда ваши данные / сообщения переносятсявам придется использовать memmove
, чтобы развернуть его и сделать смежным для функций, требующих непрерывной памяти (все стандартные функции библиотеки).
Другой вариант - использовать «умный» кольцевой буфер:выделите память для буфера, используя mmap
и отобразите один и тот же регион страниц дважды без пробелов между ними.Таким образом, чтение / запись после конца буфера продолжает чтение / запись его с самого начала, предотвращая разрыв, свойственный использованию обычного кольцевого буфера.
Рабочий пример для Linux:
#define _GNU_SOURCE 1
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
typedef struct {
size_t size;
char* data;
} SmartRingBuffer;
#define PAGE_SIZE 4096
SmartRingBuffer SmartRingBuffer_create(size_t size) {
// Round up to PAGE_SIZE.
size = (size + (PAGE_SIZE - 1)) & ~(size_t)(PAGE_SIZE - 1);
// Create a file to map.
int fd = memfd_create("smart-ring-buffer", MFD_CLOEXEC);
if(fd == -1)
abort();
if(ftruncate(fd, size))
abort();
// Reserve a contiguous region of memory twice the size, so that the second mmap with MAP_FIXED cannot fail.
char* p = mmap(NULL, 2 * size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(p == MAP_FAILED)
abort();
// Re-map the first half of the memory mapping over the second.
char* p2 = mmap(p + size, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, 0);
if(p2 == MAP_FAILED)
abort();
close(fd);
SmartRingBuffer r;
r.size = size;
r.data = p;
return r;
}
void SmartRingBuffer_destroy(SmartRingBuffer buf) {
munmap(buf.data, buf.size * 2);
}
int main() {
SmartRingBuffer buf = SmartRingBuffer_create(1);
snprintf(buf.data, buf.size, "abc"); // Format into the 1st half.
printf("%s\n", buf.data + buf.size); // Print the duplicate 2nd half.
SmartRingBuffer_destroy(buf);
return 0;
}
Вот как выглядит буфер в выводе pmap -x <pid>
:
Address Kbytes RSS Dirty Mode Mapping
00007f4c57154000 4 4 4 rw-s- memfd:smart-ring-buffer (deleted)
00007f4c57155000 4 4 4 rw-s- memfd:smart-ring-buffer (deleted)
Обратите внимание, что mmap
довольно дорого, поэтому вы можете продолжать повторное использование SmartRingBuffer
s в максимально возможной степени.(хранить их в пуле), а не создавать / уничтожать его для каждого нового соединения.