[英]malloc setting errno to EAGAIN
考虑以下程序:
#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
MCL_FUTURE | MCL_CURRENT
成功运行。 我假设权限不足或标志错误,但没有返回EPERM和EINVAL。
该错误未在函数的手册页中指定,也未在mlockall的POSIX规范中指定。 在mlockall之后放置printf表明它是malloc谁设置errno。
更奇怪的是,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
你的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
返回时看到的。
(使用PROT_NONE
额外的mmap
调用似乎是为了保留一些地址空间以供将来使用,并帮助确保将来的分配以适当的方式对齐。请参阅glibc源代码中的malloc/arena.c
以获取详细信息。它们也会失败在这种情况下,但那不是那么相关。)
简而言之,问题是malloc
试图向操作系统询问比用户请求的内存量大得多的内存。 这是为了提高效率,因为在大多数情况下,您将继续分配更多的小块内存,并且您不希望为每个内存进行系统调用。 但是这个数量超过了锁定内存的限制,所以它失败了。 EAGAIN
是在这种情况下由mmap
系统调用设置的错误代码。
也许malloc
手册页应该提到这种可能的errno
设置,但是更高级的库函数并没有描述底层系统调用可以设置errno
所有可能方式。 (例如, fprintf(3)
调用write(2)
,如果磁盘已满,可以将errno
为ENOSPC
,但在fprintf(3)
手册页中你不会发现任何提及。)你只是应该知道。
如果你想使用mlockall(MCL_FUTURE)
,那么你可能不打算使用malloc(3)
来分配内存。 您必须从sbrk(2)
或mmap(2)
手动获取它,当然,计划将其保持在适当的限制之下或优雅地失败。 这是非常不方便和限制的,所以如果你需要一些锁定的内存,并且你不是root,你可能只想在足够小的对象上使用mlock(2)
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.