MREMAP - Linux手册页

时间:2019-08-20 17:59:01  来源:igfitidea点击:

Linux程序员手册 第2部分
更新日期: 2020-06-09

名称

mremap-重新映射虚拟内存地址

语法

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <sys/mman.h>

void *mremap(void *old_address, size_t old_size,
             size_t new_size, int flags, ... /* void *new_address */);

说明

mremap()扩展(或收缩)现有的内存映射,有可能同时移动它(由flags参数和可用的虚拟地址空间控制)。

old_address是您要扩展(或收缩)的虚拟内存块的旧地址。请注意,old_address必须与页面对齐。 old_size是虚拟内存块的旧大小。 new_size是调整大小后请求的虚拟内存块大小。可以提供一个可选的第五个参数new_address。请参阅下面的MREMAP_FIXED的说明。

如果old_size的值为零,并且old_address引用了可共享的映射(请参阅mmap(2)MAP_SHARED),则mremap()将创建相同页面的新映射。 new_size将是新映射的大小,并且可以使用new_address指定新映射的位置;请参阅下面的MREMAP_FIXED的说明。如果通过此方法请求新的映射,则还必须指定MREMAP_MAYMOVE标志。

标志的位掩码参数可以为0,或包含以下标志:

MREMAP_MAYMOVE
默认情况下,如果没有足够的空间在当前位置展开映射,则mremap()失败。如果指定了此标志,则如有必要,允许内核将映射重定位到新的虚拟地址。如果重新分配了映射,则指向旧映射位置的绝对指针将变为无效(应采用相对于映射起始地址的偏移量)。
MREMAP_FIXED(since Linux 2.3.31)
该标志的作用与mmap(2)的MAP_FIXED标志相似。如果指定了此标志,则mremap()接受第五个参数void ** new_address,它指定映射必须移至的页面对齐地址。在new_address和new_size指定的地址范围内的所有先前映射都未映射。
如果指定了MREMAP_FIXED,则还必须指定MREMAP_MAYMOVE。
MREMAP_DONTUNMAP(since Linux 5.7)
该标志(必须与MREMAP_MAYMOVE结合使用)将映射重新映射到新地址,但不会在old_address上取消映射。
MREMAP_DONTUNMAP标志只能与私有匿名映射一起使用(请参阅mmap(2)中的MAP_PRIVATE和MAP_ANONYMOUS描述)。
完成后,对old_address和old_size指定的范围的任何访问都将导致页面错误。如果该地址在先前向userfaultfd(2)注册的范围内,则将由userfaultfd(2)处理程序处理该页面错误。否则,内核将分配一个零填充页面来处理故障。
MREMAP_DONTUNMAP标志可用于在保留源映射的同时原子地移动映射。有关MREMAP_DONTUNMAP的某些可能的应用程序,请参见注释。

如果由old_address和old_size指定的内存段已锁定(使用mlock(2)或类似方法),则在重新调整该段的大小和/或重新定位该段时将保持此锁定。结果,该进程锁定的内存量可能会更改。

返回值

成功时,mremap()返回一个指向新虚拟内存区域的指针。错误时,将返回值MAP_FAILED(即(void *)-1),并且已正确设置errno。

错误说明

EAGAIN
调用方尝试扩展已锁定的内存段,但是如果不超过RLIMIT_MEMLOCK资源限制,则无法实现。
EFAULT
在old_address到old_address + old_size范围内的某些地址对于此过程是无效的虚拟内存地址。即使存在覆盖请求的整个地址空间的映射,您也可以获得EFAULT,但是这些映射是不同的类型。
EINVAL
An invalid argument was given. Possible causes are:
*
old_address没有页面对齐;
*
在标志中指定了MREMAP_MAYMOVE或MREMAP_FIXED或MREMAP_DONTUNMAP以外的值;
*
new_size为零;
*
new_size或new_address无效;
*
new_address和new_size指定的新地址范围与old_address和old_size指定的旧地址范围重叠;
*
指定了MREMAP_FIXED或MREMAP_DONTUNMAP,但未同时指定MREMAP_MAYMOVE
*
指定了MREMAP_DONTUNMAP,但是在old_address和old_size指定的范围内的一个或多个页面不是私有匿名的;
*
指定了MREMAP_DONTUNMAP,而old_size不等于new_size
*
old_size为零,而old_address未引用可共享的映射(但请参见BUGS);
*
old_size为零,并且未指定MREMAP_MAYMOVE标志。
ENOMEM
Not enough memory was available to complete the operation. Possible causes are:
*
无法在当前虚拟地址处扩展存储区域,并且未在标志中设置MREMAP_MAYMOVE标志。或者,没有足够的(虚拟)内存可用。
*
使用MREMAP_DONTUNMAP导致创建新的映射,该映射将超过可用的(虚拟)内存。或者,它将超过允许的映射的最大数量。

遵循规范

此调用是特定于Linux的,不应在旨在可移植的程序中使用。

备注

mremap()更改虚拟地址和内存页面之间的映射。这可以用来实现非常有效的realloc(3)。

在Linux中,内存分为页面。一个进程具有(一个或多个)线性虚拟内存段。每个虚拟内存段都有一个或多个到实际内存页面的映射(在页面表中)。每个虚拟内存段都有自己的保护(访问权限),如果对内存的访问不正确(例如,写入只读段),则可能会导致分段冲突(SIGSEGV)。在段之外访问虚拟内存也会导致分段冲突。

如果使用mremap()来移动或扩展被mlock(2)或等效功能锁定的区域,则mremap()调用将尽最大努力填充新区域,但如果无法填充该区域,则不会因ENOMEM而失败。

在2.4版之前,glibc没有公开MREMAP_FIXED的定义,并且mremap()的原型不允许使用new_address参数。

MREMAP_DONTUNMAP use cases

MREMAP_DONTUNMAP的可能应用程序包括:

*
非合作的userfaultfd(2):应用程序可以使用MREMAP_DONTUNMAP抽出虚拟地址范围,然后使用userfaultfd(2)处理程序来处理页面错误,这些错误随后会在过程中的其他线程触摸被拉范围内的页面时发生。
*
垃圾收集:MREMAP_DONTUNMAP可以与userfaultfd(2)结合使用以实现垃圾收集算法(例如,在Java虚拟机中)。这样的实现可能比传统的垃圾收集技术更便宜(更简单),后者涉及使用PROT_NONE保护标记页面以及SIGSEGV处理程序的标记来捕获对那些页面的访问。

BUGS

在Linux 4.14之前,如果old_size为零,并且old_address引用的映射是私有映射(mmap(2)MAP_PRIVATE),则mremap()创建一个与原始映射无关的新私有映射。在用户空间应用程序中,此行为是意料之外的,并且可能是意外的(因为mremap()的意图是基于原始映射创建新的映射)。从Linux 4.14开始,在这种情况下mremap()失败,错误EINVAL。

另外参见

brk(2),getpagesize(2),getrlimit(2),mlock(2),mmap(2),sbrk(2),malloc(3),realloc(3)

您最喜欢的关于操作系统的教科书,以获取有关分页内存的更多信息(例如,Andrew S.Tanenbaum撰写的Modern Operating Systems,Randolph Bentson撰写的Inside Linux,Maurice J.Bach撰写的UNIX操作系统设计)

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/