mlock() - Unix,Linux系统调用
广告
名称mlock、munlock、mlockall、munlockall - 锁定和解锁内存概要
#include <sys/mman.h>
int mlock(const void *addr, size_t len);
int munlock(const void *addr, size_t len);
int mlockall(int flags);
int munlockall(void);
|
描述
mlock() 和 mlockall() 分别将调用进程的虚拟地址空间的部分或全部锁定到 RAM 中,防止该内存被分页到交换区。munlock() 和 munlockall() 执行相反的操作,分别解锁调用进程的虚拟地址空间的部分或全部,以便内核内存管理器在需要时可以再次将指定虚拟地址范围内的页面交换出去。内存锁定和解锁以整个页面的单位执行。mlock() 和 munlock()
mlock() 锁定从addr开始,持续len字节的地址范围内的页面。当调用成功返回时,包含指定地址范围一部分的所有页面都保证驻留在 RAM 中;这些页面保证会留在 RAM 中,直到以后解锁。
munlock() 解锁从addr开始,持续len字节的地址范围内的页面。此调用之后,内核可以再次将包含指定内存范围一部分的所有页面移动到外部交换空间。 mlockall() 和 munlockall()
mlockall() 锁定映射到调用进程地址空间中的所有页面。这包括代码、数据和堆栈段的页面,以及共享库、用户空间内核数据、共享内存和内存映射文件。当调用成功返回时,所有映射的页面都保证驻留在 RAM 中;这些页面保证会留在 RAM 中,直到以后解锁。flags参数通过一个或多个以下常量的按位或运算来构造
标签 | 描述 |
MCL_CURRENT | 锁定当前映射到进程地址空间中的所有页面。 |
MCL_FUTURE | 锁定将来将映射到进程地址空间中的所有页面。例如,这些可能是不断增长的堆和堆栈所需的新页面,以及新的内存映射文件或共享内存区域。 |
如果指定了MCL_FUTURE,则以后的系统调用(例如,mmap(2)、sbrk(2)、malloc(3))可能会失败,如果它会导致锁定的字节数超过允许的最大值(见下文)。在相同情况下,堆栈增长也可能失败:内核将拒绝堆栈扩展并向进程发送SIGSEGV信号。
munlockall() 解锁映射到调用进程地址空间中的所有页面。 备注内存锁定有两个主要应用:实时算法和高安全数据处理。实时应用程序需要确定性计时,并且与调度一样,分页是导致程序执行意外延迟的主要原因之一。实时应用程序通常还会切换到具有sched_setscheduler(2)的实时调度程序。加密安全软件通常将关键字节(如密码或密钥)处理为数据结构。由于分页,这些秘密可能被转移到持久性交换存储介质上,在那里它们可能在安全软件已擦除 RAM 中的秘密并终止后很长时间内被敌人访问。(但请注意,无论内存锁定如何,笔记本电脑和某些台式电脑上的挂起模式都会将系统 RAM 的副本保存到磁盘上。)正在使用mlockall() 来防止页面错误延迟的实时进程应该在进入时间关键部分之前预留足够的已锁定堆栈页面,以便函数调用不会导致页面错误。这可以通过调用分配足够大的自动变量(数组)并写入该数组占用的内存以访问这些堆栈页面来实现。这样,将为堆栈映射足够的页面,并可以将其锁定到 RAM 中。虚拟写入确保即使在关键部分也不会发生写时复制页面错误。 内存锁不会被通过fork(2)创建的子进程继承,并且会在execve(2)期间或进程终止时自动删除(解锁)。 如果通过munmap(2)取消映射地址范围,则该地址范围上的内存锁将自动删除。 内存锁不会堆叠,即,通过调用mlock() 或mlockall() 多次锁定的页面将通过对相应范围的单个munlock() 调用或通过munlockall() 来解锁。只要至少在一个位置或至少一个进程中锁定,映射到多个位置或多个进程的页面就会保持锁定在 RAM 中。 Linux备注在 Linux 下,mlock() 和munlock() 会自动将addr向下舍入到最接近的页面边界。但是,POSIX.1-2001允许实现要求addr与页面对齐,因此可移植应用程序应确保这一点。限制和权限在 Linux 2.6.8 及更早版本中,进程必须具有特权(CAP_IPC_LOCK)才能锁定内存,并且RLIMIT_MEMLOCK软资源限制定义了进程可以锁定的内存量限制。从 Linux 2.6.9 开始,对特权进程可以锁定的内存量没有限制,而RLIMIT_MEMLOCK软资源限制改为定义非特权进程可以锁定的内存量限制。 返回值成功时,这些系统调用返回 0。发生错误时,返回 -1,errno 设置为适当的值,并且不会更改进程地址空间中的任何锁。错误
标签 | 描述 |
ENOMEM | (Linux 2.6.9 及更高版本) 调用者具有非零RLIMIT_MEMLOCK软资源限制,但尝试锁定的内存超过允许的限制。如果进程具有特权(CAP_IPC_LOCK),则不会强制执行此限制。 |
ENOMEM | (Linux 2.4 及更早版本) 调用进程尝试锁定的内存超过 RAM 的一半。 |
EPERM | (Linux 2.6.9 及更高版本) 调用者没有特权(CAP_IPC_LOCK)并且其RLIMIT_MEMLOCK软资源限制为 0。 |
EPERM | (Linux 2.6.8 及更早版本) 调用进程没有足够的权限来调用munlockall()。在 Linux 下,需要CAP_IPC_LOCK功能。 |
对于mlock() 和munlock() |
EINVAL |
len为负。 |
EINVAL | (不在 Linux 上) addr不是页面大小的倍数。 |
ENOMEM | 指定地址范围的某些部分不对应于进程地址空间中映射的页面。 |
对于mlockall() |
EINVAL | 指定了未知的flags。 |
对于munlockall() |
EPERM | (Linux 2.6.8 及更早版本) 调用者没有特权(CAP_IPC_LOCK)。 |
缺陷在 2.4 系列 Linux 内核(包括 2.4.17)中,一个缺陷导致mlockall() MCL_FUTURE标志在fork(2) 中继承。这在内核 2.4.18 中得到纠正。从内核 2.6.9 开始,如果特权进程调用mlockall(MCL_FUTURE),然后降低特权(例如,通过将其有效 UID 设置为非零值来丢失CAP_IPC_LOCK功能),则如果遇到RLIMIT_MEMLOCK资源限制,则以后的内存分配(例如,mmap(2)、brk(2))将失败。 可用性在可使用mlock() 和munlock() 的 POSIX 系统上,_POSIX_MEMLOCK_RANGE在 <unistd.h> 中定义,并且页面中的字节数可以通过 <limits.h> 中的常量PAGESIZE(如果已定义)或通过调用sysconf(_SC_PAGESIZE)来确定。在可使用mlockall() 和munlockall() 的 POSIX 系统上,_POSIX_MEMLOCK在 <unistd.h> 中定义为大于 0 的值。(另见sysconf(3))。 符合标准POSIX.1-2001, SVr4另请参见
广告
|