将内存映射到另一个地址

4

X86-64,Linux,Windows。

假设我想要创建某种“标记指针的免费启动”。基本上,我想要有两个指针指向同一个实际的内存块,但其位不同。(例如,我想要一个位被GC收集或用于其他原因)。

intptr_t ptr = malloc() 
intptr_t ptr2 = map(ptr | GC_FLAG_REACHABLE) //some magic call

int* p = int*(ptr);
int* p2 = int*(ptr2);
*p = 10;
*p2 = 20;
assert(*p == 20)
assert(p != p2)
3个回答

2

在Linux上,可以将同一文件两次映射到内存中。在Windows上也是如此,但它有自己的一套函数来实现这个功能。


2
将同一内存(在 POSIX 上使用 mmap,如 Ignacio 所提到的,在 Windows 上使用 MapViewOfFile)映射到多个虚拟地址可能会带来一些有趣的相干难题(在一个地址写入的内容是否在另一个地址读取时可见?)。或者也可能不会。我不确定所有平台的保证是什么。
更常见的做法是,简单地在指针中保留几位,并根据需要进行移位。
如果所有对象都对齐到 8 字节边界,则通常只需在指针的三个最低有效位中存储标记,并在解引用之前屏蔽它们(正如 thkala 所提到的)。如果选择更高的对齐方式,例如 16 字节或 32 字节,则可以使用 3 或 5 个最低有效位进行标记。同样,选择一些最高有效位进行标记,并在解引用之前进行移位。(有时会使用非连续的位,例如将指针打包到 IEEE-754 浮点数(2 的 23 次方值)或双精度浮点数(2 的 51 次方值)的信号 NaN 中。)
在指针的高端继续进行,当前的x86-64实现最多使用64位指针的48位(0x0000000000000000-0x00007fffffffffff + 0xffff800000000000-0xffffffffffffffff),Linux和Windows仅向用户空间分配第一个范围内的地址,留下了可以安全屏蔽的17个最高有效位。(尽管这既不可移植,也不能保证将来依然如此。)
另一种方法是停止考虑“指针”,而是直接使用较大内存数组中的索引,就像JVM使用-XX:+UseCompressedOops一样。如果您分配了一个512MB的池并存储8字节对齐的对象,则有226个可能的对象位置,因此32位值除了索引还有6位可用。解引用将需要将索引乘以对齐方式加到数组的基地址上,该基地址在其他地方保存(对于每个“指针”都是相同的)。仔细观察,这实际上是前一种技术的概括(其基址始终为0,在那里与真实指针对齐)。

1

从前,我曾经在一个Prolog实现上工作过,它使用以下技术来在指针中留出一些空余的位:

  • 分配一个已知对齐方式的内存区域。通常情况下,malloc()会分配具有4字节或8字节对齐方式的内存。如果需要,可以使用posix_memalign()获取更高对齐大小的区域。

  • 由于生成的指针按照多个字节的间隔对齐,但它表示的是字节精确的地址,因此您将拥有一些空闲的位,在内存区域指针中这些位将被定义为零。例如,4字节对齐会在指针的LSB侧给您两个空闲位。

  • 您可以使用OR (|)运算符将标志与这些位相结合,从而得到一个标记指针。

  • 只要在使用指针进行内存访问之前正确地屏蔽指针,您就应该完全没有问题。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接