如何正确地将 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_t
和 uint64
扩展成类似:
{
int val1;
unsigned long int val2;
}
写入后 mmap
ing 会出错
将这种结构写入文件并在之后使用 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]
);
我在内存中使用了以下 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_t
和 uint64
扩展成类似:
{
int val1;
unsigned long int val2;
}
写入后 mmap
ing 会出错
将这种结构写入文件并在之后使用 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]
);