Сокеты C - recv () и recvfrom () - записывают в память, используя заданный модуль, а не последовательно? - PullRequest
1 голос
/ 27 сентября 2019

Есть ли какой-нибудь вариант, чтобы они записывали в память, используя заданный модуль (для реализации кольцевого буфера), а не последовательно?

1 Ответ

2 голосов
/ 27 сентября 2019

Одной из опций является использование ввода / вывода сбора / разброса с 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 в максимально возможной степени.(хранить их в пуле), а не создавать / уничтожать его для каждого нового соединения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...