考虑以下计划:
#include <sys/mman.h>
#include <stdlib.h>
#include <errno.h>
int
main()
{
errno = 0;
mlockall(MCL_FUTURE);
char *a = malloc(1);
if (!a)
exit(errno);
munlockall();
exit(0);
}
以普通用户身份运行时,我得到:
~ ./a.out
~ echo $?
11
来自/usr/include/asm-generic/errno-base.h
:
#define EAGAIN 11 /* Try again */
以root身份运行或通过MCL_FUTURE | MCL_CURRENT
时,它成功运行。我假设权限不足或标志错误,但没有返回EPERM和EINVAL。
在这两个函数的手册页中都没有指定该错误,也没有在mlockall的POSIX规范中指定。在mlockall之后放置printf会显示正在设置errno的malloc。
更奇怪的是,malloc似乎没有设置EAGAIN(或者我在错误的地方寻找):
/usr/src/glibc/glibc-2.19/malloc grep -r . -e EAGAIN
那么这笔交易是什么?
~ uname -r 18:15:04
3.16-2-486
~ gcc --version 18:15:05
gcc (Debian 4.9.2-10) 4.9.2
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
~ ldd --version 18:15:11
ldd (Debian GLIBC 2.19-18+deb8u1) 2.19
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
~ 18:15:15
答案 0 :(得分:4)
您的mlockall()
来电要求锁定所有未来的内存分配。但是,操作系统设置了可由任何一个非特权进程锁定的最大内存量。您可以使用getrlimit(RLIMIT_MEMLOCK,...)
查询此金额。在我的系统上它是65536字节。
现在,当我在我的系统上运行程序时,使用strace(1)
查看进行了哪些系统调用,我得到以下内容:
mlockall(MCL_FUTURE) = 0
brk(0) = 0x2318000
brk(0x2339000) = 0x2318000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
mmap(NULL, 134217728, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = -1 EAGAIN (Resource temporarily unavailable)
exit_group(11) = ?
因此malloc
首先使用brk
尝试分配135168个字节(0x2339000-0x2318000
)。这失败是因为它超出了锁定限制,因此brk
保持“断点”(进程数据段的顶部)不变。 (请参阅brk(2)
手册页中有关C库和brk()
内核版本之间不同约定的说明。)
malloc
然后尝试使用mmap
分配1048576个字节。这也失败了(因为它超过65536字节),在这里我们看到返回的EAGAIN
错误代码。 mmap(2)
文档的手册页errno
设置为EAGAIN
如果“文件已被锁定,或者内存已锁定太多”,后者的情况就是这里的情况。与许多库函数一样,malloc
将传递它所进行的系统调用留下的errno
值,因此EAGAIN
是malloc
返回时看到的内容。
(使用mmap
的额外PROT_NONE
来电似乎是为了保留一些地址空间以供将来使用,并有助于确保未来的分配以适当的方式对齐。请参阅malloc/arena.c
在glibc源代码中有关于血腥的细节。在这种情况下它们也失败了,但那并不那么重要。)
简而言之,问题是malloc
尝试向操作系统询问的内存量大于用户所要求的内存量。这是为了提高效率,因为在大多数情况下,您将继续分配更多的小块内存,并且您不希望为每个内存进行系统调用。但是这个数量超过了锁定内存的限制,所以它失败了。 EAGAIN
是在这种情况下由mmap
系统调用设置的错误代码。
也许malloc
手册页应该提到这个可能的errno
设置,但是更高级的库函数并没有描述errno
可以设置的所有可能方式。底层系统调用。 (例如,fprintf(3)
调用write(2)
,如果磁盘已满,可以将errno
设置为ENOSPC
,但在{{{ 1}}手册页。)你应该知道。
如果您想使用fprintf(3)
,那么您可能无法使用mlockall(MCL_FUTURE)
计划分配内存。您必须从malloc(3)
或sbrk(2)
手动获取,当然,计划将其保持在适当的限制或优雅地失败。这是非常不方便和限制性的,所以如果你需要一些锁定内存,并且你不是root用户,你可能只想在足够小的对象上使用mmap(2)
。
答案 1 :(得分:0)
答案 2 :(得分:0)
mlockall()
的返回值是什么?
[EAGAIN]
进行呼叫时,无法锁定操作识别的部分或全部内存。
EAGAIN Some or all of the specified address range could not be locked.