有没有操作系统可以在不实际复制内存的情况下将内存从一个地址移动到另一个地址?

5

memcpy/memmove是将数据从源复制到目的地的函数。是否存在一种方法可以将页面从一个虚拟地址移动到另一个虚拟地址,而不需要实际逐字节复制源数据?我认为这完全有可能,但任何操作系统实际上允许这样做吗?对我来说,动态数组是如此广泛和流行的概念,但通过物理复制来扩展它们却是一种浪费的操作,当你开始谈论以GB为单位的数组大小时,这种效果就变得不可接受了(例如,想象一下将100GB数组扩展为200GB数组。现在在低于10K美元的服务器上完全有可能出现这个问题)。

void* very_large_buffer = VirtualAlloc(NULL, 2GB, MEM_COMMIT);
// Populate very_large_buffer, run out of space.
// Allocate buffer twice as large, but don't actually allocate 
// physical memory, just reserve the address space.
void* even_bigger_buffer = VirtualAlloc(NULL, 4GB, MEM_RESERVE);
// Remap the physical memory from very_large_buffer to even_bigger_buffer without copying
// (i.e. don't copy 2GB of data, just copy the mapping of virtual pages to physical pages)
// Does any OS provide support for an operation like this?    
MoveMemory(very_large_buffer, even_bigger_buffer, 2GB)
// Now very_large_buffer no longer has any physical memory pages associated with it
VirtualFree(very_large_buffer)
4个回答

6
在Linux上,您可以使用mremap在某种程度上实现这一点。该调用会操作进程的页表以进行零拷贝重分配(如果可能)。但并非所有情况都可以实现(地址空间碎片化和已存在的映射是问题)。实际上,手册中就是这么说的:“mremap()更改虚拟地址和内存页面之间的映射关系。这可用于实现非常高效的realloc(3)。”

这看起来正是我所需要的。我可以在Linux上使用它,在其他地方则可以使用标准的内存复制。 - Eloff
1
或者使用realloc,并希望它在底层使用类似的方法。 :) - onitake

2
今日免费次数已满, 请开通会员/明日再来

1

每个POSIX系统都能够做到这一点。如果您使用带有文件描述符(通过openshm_open获得)而不是匿名的mmap,则可以取消映射它,然后截断(缩小或增大),然后再次映射它。您可能会经常为相同的页面获得不同的虚拟地址。


请纠正我,但是取消映射会将所有脏页写回到该文件中(懒惰地?)因此,当您重新映射它时,访问页面可能需要从文件中读取它们。其中有很多实现细节,但通过引起磁盘IO来避免内存复制不太可能获胜。不过我很好奇,因为mmap通常用于在进程之间共享内存,所以应该可以增大(而不是缩小)文件并创建第二个映射而无需关闭第一个映射吧?那将是零移动复制,我认为。 - Eloff
首先,如果您使用shm_open,那么就没有文件,因此无法将任何内容写入其中。即使对于open,我也不认为取消映射会写回页面,只有关闭文件描述符才能强制执行这一点。 - Jens Gustedt
我猜在shm_open的情况下,你不会因为取消映射而丢失内容,这很有趣。而且你说得对,我猜脏页也不会因为关闭映射而被写入,它应该仍然在系统写缓冲区中。 - Eloff

0

我的意思是,你永远无法绝对保证下一个100GB中没有活动内存,因此你可能无法使其连续。

另一方面,您可以使用不规则数组(数组的数组),其中数组不必相邻(甚至大小不同)。许多动态数组的优点可能无法扩展到100GB领域。


1
我的理解是实际物理RAM页面不需要连续,这就是虚拟寻址的意义所在。但你需要200GB的非碎片化虚拟地址空间(目前64位进程在现代操作系统中有8TB的地址空间,所以这不应该是一个问题)。 - Eloff

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