MLOCK - Linux手册页
Linux程序员手册 第2部分
更新日期: 2020-04-11
名称
mlock,mlock2,munlock,mlockall,munlockall-锁定和解锁内存
语法
#include <sys/mman.h> int mlock(const void *addr, size_t len); int mlock2(const void *addr, size_t len, int flags); int munlock(const void *addr, size_t len); int mlockall(int flags); int munlockall(void);
说明
mlock(),mlock2()和mlockall()将部分或全部调用进程的虚拟地址空间锁定到RAM中,以防止将该内存分页到交换区。
munlock()和munlockall()执行相反的操作,解锁部分或全部调用进程的虚拟地址空间,以便内核内存管理器需要时,可以再次换出指定虚拟地址范围内的页面。
内存锁定和解锁以整页为单位执行。
mlock(), mlock2(), and munlock()
mlock()将页面锁定在地址范围内,该地址范围从addr开始并持续len个字节。当调用成功返回时,保证所有包含指定地址范围一部分的页面都驻留在RAM中;这些页面保证保留在RAM中,直到以后被解锁为止。
mlock2()还将页面锁定在指定范围内,从addr开始并持续len个字节。但是,调用成功返回后,该范围内包含的页面的状态将取决于flags参数中的值。
flags参数可以是0或以下常量:
- MLOCK_ONFAULT
- 锁定当前驻留的页面并标记整个范围,以便在剩余的非驻留页面由页面错误填充时被锁定。
如果标志为0,则mlock2()的行为与mlock()完全相同。
munlock()解锁地址范围内的页面,该地址范围从addr开始并持续len个字节。调用之后,内核可以将包含部分指定内存范围的所有页面再次移至外部交换空间。
mlockall() and munlockall()
mlockall()锁定所有映射到调用进程地址空间的页面。这包括代码,数据和堆栈段的页面,以及共享库,用户空间内核数据,共享内存和内存映射文件。当调用成功返回时,保证所有映射的页面都驻留在RAM中;页面将保证保留在RAM中,直到以后被解锁为止。
flags参数构造为以下一个或多个常量的按位或:
- MCL_CURRENT
- 锁定当前映射到进程地址空间的所有页面。
- MCL_FUTURE
- 锁定所有将来将映射到进程地址空间的页面。例如,这些可能是不断增长的堆和堆栈所需的新页面,以及新的内存映射文件或共享内存区域。
- MCL_ONFAULT(since Linux 4.4)
- 与MCL_CURRENT和/或MCL_FUTURE一起使用。标记所有当前(带有MCL_CURRENT)或将来(带有MCL_FUTURE)的映射以在错误页面锁定时将其锁定。与MCL_CURRENT一起使用时,所有当前页面均被锁定,但是mlockall()在不存在的页面上不会出错。与MCL_FUTURE一起使用时,所有将来的映射都将在出现故障时标记为锁定页面,但是在创建映射时不会被锁定填充。 MCL_ONFAULT必须与MCL_CURRENT或MCL_FUTURE或两者一起使用。
如果已指定MCL_FUTURE,则以后的系统调用(例如mmap(2),sbrk(2),malloc(3))可能会失败,如果它会导致锁定字节数超过允许的最大值(请参阅下文) 。在相同的情况下,堆栈增长可能同样会失败:内核将拒绝堆栈扩展,并将SIGSEGV信号传递给进程。
munlockall()解锁映射到调用进程地址空间中的所有页面。
返回值
成功时,这些系统调用将返回0。错误时,将返回-1,正确设置errno,并且不会更改进程地址空间中的任何锁。
错误说明
- ENOMEM
- (Linux 2.6.9和更高版本)调用方的RLIMIT_MEMLOCK软资源限制为非零,但试图锁定的内存超出了允许的限制。如果进程具有特权(CAP_IPC_LOCK),则不会强制执行此限制。
- ENOMEM
- (Linux 2.4和更早版本)调用过程试图锁定一半以上的RAM。
- EPERM
- 调用方没有特权,但是需要特权(CAP_IPC_LOCK)才能执行请求的操作。
对于mlock(),mlock2()和munlock():
- EAGAIN
- 部分或全部指定的地址范围无法锁定。
- EINVAL
- 加法器addr + len的结果小于addr(例如,加法可能导致溢出)。
- EINVAL
- (不适用于Linux)addr不是页面大小的倍数。
- ENOMEM
- 一些指定的地址范围与进程的地址空间中的映射页不对应。
- ENOMEM
- 锁定或解锁区域将导致具有不同属性(例如,锁定与未锁定)的映射总数超过允许的最大值。 (例如,在当前锁定的映射的中间解锁范围会导致三个映射:在两端分别有两个锁定的映射和在中间的一个解锁的映射。)
对于mlock2():
- EINVAL
- 指定了未知标志。
对于mlockall():
- EINVAL
- 指定了未知标志或指定了MCL_ONFAULT,而没有MCL_FUTURE或MCL_CURRENT。
对于munlockall():
- EPERM
- (Linux 2.6.8和更早版本)调用者没有特权(CAP_IPC_LOCK)。
版本
从Linux 4.4开始,可以使用mlock2()了。在2.27版中添加了glibc支持。
遵循规范
POSIX.1-2001,POSIX.1-2008,SVr4。
mlock2()是Linux特定的。
在可以使用mlock()和munlock()的POSIX系统上,定义_POSIX_MEMLOCK_RANGE,并且可以通过或调用sysconf(_SC_PAGESIZE)中的常量PAGESIZE(如果已定义)确定页面中的字节数。
在可使用mlockall()和munlockall()的POSIX系统上,将_POSIX_MEMLOCK定义为大于0的值。(另请参见sysconf(3)。)
备注
内存锁定有两个主要应用程序:实时算法和高安全性数据处理。实时应用程序需要确定性的时序,并且像调度一样,分页是程序意外执行延迟的主要原因之一。实时应用程序通常也将使用sched_setscheduler(2)切换到实时调度程序。密码安全软件通常将关键字节(例如密码或秘密密钥)作为数据结构来处理。作为分页的结果,这些机密可以转移到持久交换存储介质上,在安全软件擦除RAM中的机密并终止后很长一段时间,敌人就可以访问它们。 (但是请注意,无论内存锁定如何,笔记本电脑和某些台式机上的暂停模式都会将系统RAM的副本保存到磁盘上。)
使用mlockall()防止页面错误延迟的实时进程应在进入时间关键部分之前保留足够的锁定堆栈页面,以便函数调用不会引起页面错误。这可以通过调用一个函数来实现,该函数分配一个足够大的自动变量(一个数组)并写入该数组所占用的内存中,以便接触这些堆栈页面。这样,将为堆栈映射足够的页面,并且可以将其锁定到RAM中。伪写入确保关键部分甚至不会发生写时复制页面错误。
内存锁不会被通过fork(2)创建的子代继承,而是在execve(2)或进程终止时自动删除(解锁)。 mlockall()MCL_FUTURE和MCL_FUTURE | MCL_ONFAULT设置不会被通过fork(2)创建的子代继承,并且会在execve(2)期间清除。
请注意,fork(2)将为写时复制操作准备地址空间。结果是,随后的任何写访问都将导致页面错误,而页面错误又可能导致实时过程的高延迟。因此,至关重要的是,不要在mlockall()或mlock()操作之后调用fork(2)-甚至是从进程中优先级较低的线程运行的进程中也不能调用fork(2),而该进程的优先级也较高。
如果通过munmap(2)未映射地址范围,则会自动删除地址范围上的内存锁定。
内存锁不会堆叠,也就是说,通过调用mlock(),mlock2()或mlockall()多次锁定的页面将通过单个调用munlock()的相应范围或munlockall()来解锁。 )。只要映射到多个位置或多个进程的页面至少被锁定在一个位置或至少一个进程,它们就被锁定在RAM中。
如果对使用MCL_FUTURE标志的mlockall()的调用之后是另一个未指定此标志的调用,则MCL_FUTURE调用所做的更改将丢失。
mlock2()MLOCK_ONFAULT标志和mlockall()MCL_ONFAULT标志为处理大型映射的应用程序提供了有效的内存锁定,该应用程序仅触摸映射中的一小部分页面。在这种情况下,锁定映射中的所有页面将导致内存锁定的重大损失。
Linux notes
在Linux下,mlock(),mlock2()和munlock()自动将addr向下舍入到最近的页面边界。但是,mlock()和munlock()的POSIX.1规范允许实现要求addr是页面对齐的,因此可移植应用程序应确保做到这一点。
特定于Linux的/ proc / [pid] / status文件的VmLck字段显示使用mlock(),mlock2(),mlockall()和mmap(2)MAP_LOCKED锁定ID PID的进程锁定了几千字节的内存。
Limits and permissions
在Linux 2.6.8和更低版本中,必须对进程进行特权(CAP_IPC_LOCK)才能锁定内存,并且RLIMIT_MEMLOCK软资源限制定义了对该进程可以锁定的内存数量的限制。
从Linux 2.6.9开始,对特权进程可以锁定的内存量没有任何限制,而RLIMIT_MEMLOCK软资源限制则定义了对非特权进程可以锁定的内存数量的限制。
BUGS
在Linux 4.8和更早版本中,内核对非特权进程(例如,没有CAP_IPC_LOCK)的锁定内存进行核算中的错误意味着,如果addr和len指定的区域与现有锁重叠,则将计算重叠区域中已经锁定的字节检查极限时两次。这种双重记帐方式可能会错误地为超出RLIMIT_MEMLOCK限制的进程计算"总锁定内存"值,结果mlock()和mlock2()在应该成功的请求上将失败。此错误已在Linux 4.9中修复。
在2.4.17及以下的2.4系列Linux内核中,有一个错误导致mlockall()MCL_FUTURE标志在fork(2)上继承。这已在内核2.4.18中纠正。
从内核2.6.9开始,如果特权进程调用mlockall(MCL_FUTURE)并随后放弃特权(例如,通过将其有效UID设置为非零值来丢失CAP_IPC_LOCK功能),则随后进行内存分配(例如mmap(2) ,brk(2))如果遇到RLIMIT_MEMLOCK资源限制,将失败。
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。