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参见
广告
|