Как правильно записать структуру C в файл на диске, чтобы на нем можно было использовать mmap? - PullRequest
3 голосов
/ 20 апреля 2020

Я использую следующую C структуру в памяти:

typedef struct MyStructHdr
{

    char        text[4];
    int         version_num;

    uint64 init_value;

    uint64 entries[];

} MyStructHdr;

typedef MyStructHdr *MyStruct;

Поле entries[] - указатель на некоторый гибкий массив. Тип uint64 - это пользовательское переносимое приложение, специфицирующее тип c, которое добавляет поддержку uint64_t в 32-битной ОС.

Мне нужно правильно записать эту структуру в файл, чтобы я мог использовать mmap() позже (на той же платформе / ОС):

map = (MyStruct) mmap(NULL, MyStructActualSize,
                      PROT_READ | PROT_WRITE, MAP_SHARED,
                      mystruct_fd, 0);

Что мне теперь делать? Я просто пишу MyStruct поля одно за другим (и entries[] кусками через буфер), используя write(). В конце написана CRC32 контрольная сумма.

Все прекрасно работает на всех доступных мне 64-битных системах. Кажется, что первые 4 символа + 32-битный int выровнены в один 64-битный блок, а uint64 просто расширяется до uint64_t, поэтому после записи все mmap 'редактируется правильно.

Тем не менее, я боюсь, что в 32-битной системе или какой-либо другой c ОС / архитектуре, где применяются разные правила выравнивания и нет uint64_t и uint64 расширяется в нечто вроде:

{
    int val1;
    unsigned long int val2;
}

Я получу неправильный mmap 'после записи.

Что такое переносимый способ записи такой структуры в файл и использования mmap после этого?

PS На самом деле, это все о PostgreSQL расширении и uint64 здесь pg_atomic_uint64, но я думаю, что этот вопрос является более общим.

1 Ответ

1 голос
/ 20 апреля 2020

Вы не должны писать членов по одному, потому что это не будет учитывать заполнение между участниками. Напишите все сразу:

write(fd, MyStruct, sizeof(MyStructHdr) + entry_count * sizeof(uint64));

, где entry_count - количество элементов в элементе гибкого массива.

Если вам нужно написать каждый элемент отдельно для атомарности, вы можно использовать макрос offsetof, чтобы получить размер, включая отступы.

write(fd, &MyStruct->text, offsetof(MyStructHdr, version_num));
write(fd, &Mystruct->version_num, offsetof(MyStructHdr, init_value) - offsetof(MyStructHdr, version_num));
write(fd, &MyStruct->init_value, offsetof(MyStructHdr, entries) - offsetof(MyStructHdr, version_num));

, а затем записать массив MyStruct->entries кусками. Вам не нужно беспокоиться о заполнении там, потому что sizeof в элементе массива включает заполнение между элементами (это гарантирует, что sizeof array == element_count * sizeof array[0]);

...