C 将内存映射到另一个地址

C 将内存映射到另一个地址,c,memory,C,Memory,X86-64,Linux,Windows 考虑一下,我想做一些“免费启动标记指针”。基本上,我希望有两个指针指向相同的实际内存块,但其位不同。(例如,我希望GC收集或出于其他原因使用一个位) 在Linux上,mmap()将同一文件复制两次。在Windows上也一样,但它有自己的函数集。有一次,我在一个Prolog实现中使用以下技术在指针中保留备用位: 分配具有已知对齐方式的内存区域malloc()通常以4字节或8字节对齐方式分配内存。如有必要,使用以获取具有更大对齐尺寸的区域 由于生成的指针与

X86-64,Linux,Windows

考虑一下,我想做一些“免费启动标记指针”。基本上,我希望有两个指针指向相同的实际内存块,但其位不同。(例如,我希望GC收集或出于其他原因使用一个位)


在Linux上,mmap()将同一文件复制两次。在Windows上也一样,但它有自己的函数集。

有一次,我在一个Prolog实现中使用以下技术在指针中保留备用位:

  • 分配具有已知对齐方式的内存区域
    malloc()
    通常以4字节或8字节对齐方式分配内存。如有必要,使用以获取具有更大对齐尺寸的区域

  • 由于生成的指针与多个字节的间隔对齐,但它表示字节精确的地址,因此内存区域指针中有一些根据定义为零的备用位。例如,4字节对齐在指针的LSB侧提供两个备用位

  • 您或(
    |
    )使用这些位标记您的标志,现在有一个标记指针

  • 只要您在使用指针访问内存之前注意正确地屏蔽它,您应该会很好

将同一内存(
mmap
在POSIX上,正如Ignacio提到的,
MapViewOfFile
在Windows上)映射到多个虚拟地址可能会为您提供一些有趣的一致性难题(在另一个地址读取时,在一个地址的写入是否可见?)。也许不是。我不确定所有的平台保证是什么

更常见的情况是,只需在指针中保留一些位,并根据需要进行移位

如果所有对象都与8字节边界对齐,通常只需将标记存储在指针的3个最低有效位中,并在取消引用之前屏蔽它们(如thkala所述)。如果选择更高的对齐方式,例如16字节或32字节,则有3或5个最低有效位可用于标记。同样地,选择几个最重要的位进行标记,并在解引用之前将它们移开。(有时使用非连续位,例如,当将指针打包到IEEE-754的信令NAN中时,浮点(223个值)或双倍(251个值)。)

在指针的高端,x86-64的当前实现最多使用64位指针中的48位(0x0000000000000000-0x00007FFFFFFFFFFFFF+0xFFFF80000000000-0xFFFFFFFFFF),Linux和Windows只将第一个范围内的地址分配给用户空间,留下17个可以安全屏蔽的最高有效位。(不过,这既不能随身携带,也不能保证在未来保持不变。)

另一种方法是停止考虑“指针”,简单地使用索引到更大的内存数组中,就像JVM所做的那样。如果已分配512MB池并存储8字节对齐的对象,则可能有226个对象位置,因此32个值除索引外还有6位可用。解引用需要将索引乘以数组的基址,保存在别处(每个“指针”都是相同的)。如果仔细观察,这只是前面技术的一个推广(它总是以0为基数,在0中,事物与实际指针对齐)

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)