如何正确地将 C 结构写入磁盘上的文件,以便可以在其上使用 mmap?

How to properly write C structure to file on disk, so it was possible to use mmap on it?

我在内存中使用了以下 C 结构:

typedef struct MyStructHdr
{

    char        text[4];
    int         version_num;

    uint64 init_value;

    uint64 entries[];

} MyStructHdr;

typedef MyStructHdr *MyStruct;

字段entries[] 是指向某个灵活数组的指针。类型 uint64 是自定义的可移植应用程序特定类型,它在 32 位 OS.

上添加了 uint64_t 支持

我必须将这个结构正确地写入文件,以便我以后能够在其上使用 mmap()(在同一个 platform/OS 上):

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

我现在做什么?我只是使用 write() 一个一个地写入 MyStruct 个字段(并通过缓冲区按块写入 entries[] 个字段)。最后写入 CRC32 校验和。

在我可用的所有 64 位系统上一切正常。似乎前 4 个字符 + 32 位 int 对齐到单个 64 位块中并且 uint64 简单地扩展为 uint64_t,所以在写入之后一切都是 mmap'编辑正确。

但是,恐怕在 32 位系统或某些特定的 OS/architecture 上,应用了不同的对齐规则并且没有 uint64_tuint64 扩展成类似:

{
    int val1;
    unsigned long int val2;
}

写入后 mmaping 会出错

将这种结构写入文件并在之后使用 mmap 的可移植方法是什么?

P.S。实际上,这都是关于 PostgreSQL 扩展的,uint64 这里是 pg_atomic_uint64,但我认为这个问题更笼统。

你不应该一个一个地写成员,因为这不会考虑成员之间的填充。一口气写完:

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

其中 entry_count 是灵活数组成员中的元素数。

如果为了原子性需要单独编写每个成员,可以使用offsetof宏来获取包含padding的大小。

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, init_value));

然后将MyStruct->entries数组分块写入。您不必担心那里的填充,因为数组元素上的 sizeof 包括元素之间的填充(这确保 sizeof array == element_count * sizeof array[0]);