微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

将小结构分配到 64 位系统中对齐的 32 位

如何解决将小结构分配到 64 位系统中对齐的 32 位

问题:我正在实现一个非阻塞数据结构,其中线程使用 CAS 操作更改共享指针。由于指针可以回收,我们有 ABA 问题。为了避免这种情况,我想为每个指针附加一个版本。这称为版本化指针。 CAS128 被认为比 CAS64 贵,所以我尽量避免超过 64 位。

我正在尝试实现版本化指针。在 32b 系统中,版本化指针是 64b 结构,其中前 32 位是指针,后 32 位是其版本。这允许我使用 CAS64 原子地改变指针。

我在使用 64b 系统时遇到问题。在这种情况下,我仍然想使用 CAS64 而不是 CAS128,所以我试图分配一个与 4GB 对齐的指针(即 32 个零)。然后我可以使用掩码来推断指针和版本。

我尝试使用 alligned_malloc、填充和 std::align解决方案,但这些涉及分配非常大量的内存,例如,alligned_malloc(1LL << 32,(1LL << 32)* sizeof(void*)) 分配 4GB 的内存。另一种解决方案是使用内存映射文件,但这涉及我们试图避免的同步。

有没有办法分配 8B 的内存与我缺少的 4GB 对齐?

解决方法

首先,一个不可移植的解决方案,它限制了代码复杂性蔓延到分配点(参见下面的另一种方法,使使用点更复杂,但应该是可移植的);它仅适用于 POSIX 系统(不是 Windows),但您可以将开销减少到一个页面的大小(不是 8 字节,但在 64 位系统的上下文中,浪费 4088 字节不是 em> 如果您不经常这样做,那就太糟糕了;显然,您的问题的性质意味着每 4 GB 浪费的字节数不可能超过 sysconf(_SC_PAGESIZE) - 8,所以这不是太多 坏)通过以下机制:

  1. mmap 4 GB 匿名内存(非文件支持;传递 fd-1 并包含 MAP_ANONYMOUS 标志)
  2. 计算该块内 4 GB 对齐指针的地址
  3. munmap 该地址之前的内存,以及该地址之后 sysconf(_SC_PAGE_SIZE) 个字节的内存

这是可行的,因为内存映射不是单一的;它们可以零碎地取消映射,可以重新映射单个页面而不会出错,等等。

请注意,如果您的交换空间不足,那么 4 GB 的简短请求可能会导致问题(例如,在禁用启发式过量使用的 Linux 系统上,如果无法使用交换来支持它,则可能无法分配内存,即使您从未使用过大部分)。您可以尝试将 MAP_NORESERVE 传递给原始请求,然后执行取消映射,然后使用 MAP_FIXED(不带 MAP_NORESERVE)重新映射该单个页面,以确保可以使用分配而不触发 { {1}} 在撰写本文时。


如果您不能使用 POSIX SIGSEGV,如果真的无法使用 CAS128,您可能需要考虑为这些指针使用 segmented memory model 之类的 the old x86 scheme。您预先阻止分配 4 GB 段(它们不需要任何特殊对齐),并使您的“指针”与段的基地址相距 32 位 偏移;您不能使用整个 64 位地址空间(除非您允许多个选择器,例如可能通过重新利用版本号字段的一部分;您可能只能使用几百万个版本而不是 40 亿个版本),但是如果您不需要这样做,这可以让您拥有一个在分配后永远不会更改的基地址(因此不需要原子),偏移量适合您所需的 32 位字段。因此,不要通过以下方式获取您的数据:

mmap

你有一个像这样初始化的段指针:

data = *mystruct.pointer;

将它包装在一个子分配器中来分区空间,现在查找是:

char *const base_address = new char[1ULL << 32];  // Or use smart pointer of your choosing

我确信有一些巧妙的方法可以使用自定义分配器、自定义 data = *reinterpret_cast<REAL_TYPE_HERE*>(&base_address[mystruct.pointer]); 来更好地包装它,你有什么,但我从来没有在 C++ 中这样做过(我做过类似的魔术在 C 语言中,没有工具使它“漂亮”),我可能会弄错,所以我将把它留作练习。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。