malloc将errno设置为EAGAIN

时间:2015-12-08 20:21:38

标签: c linux malloc

考虑以下计划:

#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

3 个答案:

答案 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值,因此EAGAINmalloc返回时看到的内容。

(使用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)

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

手册页

mlockall

malloc

答案 2 :(得分:0)

mlockall()的返回值是什么?

the POSIX standard

  

[EAGAIN]

     

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

根据Linux man page

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