繁体   English   中英

malloc将errno设置为EAGAIN

[英]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值,因此EAGAINmalloc返回时看到的。

(使用PROT_NONE额外的mmap调用似乎是为了保留一些地址空间以供将来使用,并帮助确保将来的分配以适当的方式对齐。请参阅glibc源代码中的malloc/arena.c以获取详细信息。它们也会失败在这种情况下,但那不是那么相关。)

简而言之,问题是malloc试图向操作系统询问比用户请求的内存量大得多的内存。 这是为了提高效率,因为在大多数情况下,您将继续分配更多的小块内存,并且您不希望为每个内存进行系统调用。 但是这个数量超过了锁定内存的限制,所以它失败了。 EAGAIN是在这种情况下由mmap系统调用设置的错误代码。

也许malloc手册页应该提到这种可能的errno设置,但是更高级的库函数并没有描述底层系统调用可以设置errno所有可能方式。 (例如, fprintf(3)调用write(2) ,如果磁盘已满,可以将errnoENOSPC ,但在fprintf(3)手册页中你不会发现任何提及。)你只是应该知道。

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

  1. mlockall正在设置错误号,原因部分或全部指定的地址范围无法锁定。
  2. 标准malloc()在失败时不会将errno设置为EAGAIN。

男人页

mlockall用于

的malloc

mlockall()的返回值是mlockall()

根据POSIX标准

[EAGAIN]

在进行呼叫时,无法锁定由操作识别的部分或全部存储器。

根据Linux手册页

  EAGAIN Some or all of the specified address range could not be locked. 

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM