gpt4 book ai didi

c - malloc 将 errno 设置为 EAGAIN

转载 作者:IT王子 更新时间:2023-10-29 01:21:08 25 4
gpt4 key购买 nike

考虑以下程序:

#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 表明是 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() 调用要求锁定所有 future 的内存分配。但是,操作系统设置了可以被任何一个非特权进程锁定的最大内存量。您可以使用 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) 手册页中有关 brk() 的 C 库和内核版本之间的不同约定的说明。)

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),如果磁盘已满,但您不会在 fprintf(3) 手册页中找到任何提及。)您应该知道。

如果您想使用mlockall(MCL_FUTURE),那么您可能无法计划使用malloc(3) 分配内存。您必须从 sbrk(2)mmap(2) 手动获取它,当然,要计划将其保持在适当的限制下或优雅地失败。这是非常不方便和限制性的,所以如果您需要一些锁定的内存,并且您不是 root,您可能只想对足够小的对象使用 mlock(2)

关于c - malloc 将 errno 设置为 EAGAIN,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34165276/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com