x64系统中能否利用uint64_t子位存储指针可靠访问mmap缓冲区?
自定义动态内存分配器:64位元数据字段存储指针、大小与标志的可靠实现
问题描述
我正在构建一个自定义动态内存分配器,使用大小为8 << 20(8MiB)的mmap缓冲区作为堆。mmap缓冲区的实现代码如下:
memory_buffer::memory_buffer() { void* buf = mmap(nullptr, // maybe set a 40bit addressable addr? this->size, // Buffer should be 8 MiB big PROT_WRITE, // We want to read and write the buffer MAP_ANON | MAP_PRIVATE, -1, 0); assert(buf != MAP_FAILED); this->buffer = (char*) buf; }
我预留了缓冲区的前2048字节存储空闲分配元数据,剩余部分作为可分配堆空间。我的需求是:
- 元数据结构体尽可能小;
- 结构体大小为64*n位(出于对齐考虑)。
我计划使用如下元数据结构体并拷贝到mmap缓冲区中:
struct freed_allocation{ uint64_t ptr_and_size_and_flag; //bitmasks to access relevant fields. (flag:1bit, size:23bit) }
我希望用剩余的40位存储缓冲区位置指针,请问是否存在可靠的实现方法?
可靠实现方案
你的思路完全可行,核心是用偏移量替代完整指针存储,结合固定的缓冲区基地址实现指针的可靠转换,具体方案如下:
1. 用偏移量替代完整指针
由于所有可分配内存都在同一块连续的8MiB缓冲区中,不需要存储完整的64位指针,只需要存储空闲块相对于缓冲区基地址的偏移量即可。8MiB的最大偏移量仅需23位就能容纳,远小于你预留的40位空间,完全满足需求且有冗余空间可用于扩展。
2. 位段划分与封装操作
按照你的规划,对uint64_t字段做统一的位划分(以下示例从低位到高位排列,可根据习惯调整):
- 第0位(最低位):空闲标志位(1表示空闲,0表示已分配)
- 第1~23位:空闲块大小(共23位,最大支持8MiB,匹配缓冲区容量)
- 第24~63位:空闲块相对于缓冲区基地址的偏移量(共40位,冗余空间可留作后续扩展)
用宏和内联函数封装位操作,避免直接写位掩码出错:
// 位掩码定义 #define FLAG_MASK 0x1ULL #define SIZE_MASK 0x7FFFFFULL #define OFFSET_MASK 0xFFFFFFFFFF000000ULL // 提取字段 static inline bool get_is_free(uint64_t data) { return (data & FLAG_MASK) != 0; } static inline size_t get_block_size(uint64_t data) { return (data >> 1) & SIZE_MASK; } static inline size_t get_block_offset(uint64_t data) { return (data >> 24) & ((1ULL << 40) - 1); } // 打包元数据 static inline uint64_t pack_metadata(size_t offset, size_t size, bool is_free) { uint64_t data = 0; data |= (static_cast<uint64_t>(offset) << 24); data |= (static_cast<uint64_t>(size) << 1); data |= is_free ? 1 : 0; return data; }
3. 偏移量与指针的转换
从偏移量转回实际指针时,直接用缓冲区基地址加上偏移量即可,只要不munmap该缓冲区,这个转换就是完全可靠的:
char* buffer_base = this->buffer; // mmap缓冲区的基地址 size_t block_offset = get_block_offset(freed_block->ptr_and_size_and_flag); void* block_ptr = buffer_base + block_offset;
额外注意事项
- 你的
freed_allocation结构体大小为8字节(64位),天然满足64位对齐要求,符合需求; - 位段划分规则要全局统一,避免因位序混乱导致的错误;
- 冗余的17位偏移量空间可后续用于存储额外标记(如块类型、合并标记等)。
内容的提问来源于stack exchange,提问作者6charm
相关产品推荐
相关产品推荐

