C语言 MuslMapENOMEM

qltillow  于 2023-01-20  发布在  其他
关注(0)|答案(2)|浏览(89)

我编译了一些针对musl的交叉编译器(x86_64,i686,arm)。我需要编译代码,分配像2048 Mb +-200 Mb。然而,我注意到一些错误与i686 musl编译器:

#include <stdio.h>
#include <sys/mman.h>
#include <sys/resource.h>
#include <errno.h>
int main(){
  void*ptr = mmap(0, 2147483647,PROT_READ|PROT_WRITE, 
            MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);       // malloc(2147483647)
  if(ptr == MAP_FAILED){
    printf("ERROR MMAP - "); 
    if(errno == ENOMEM){
      struct rlimit ra, rd; 
      if( getrlimit(RLIMIT_AS, &ra) < 0)
        printf("RLIMIT AS ERR\n");  
      if( getrlimit(RLIMIT_DATA, &rd) < 0)
        printf("RLIMIT AS ERR\n");   
      printf("ENOMEM, RLIMIT_AS: (%lu:%lu); RLIMIT_DATA: (%lu:%lu)\n", ra.rlim_cur, ra.rlim_max, rd.rlim_cur, rd.rlim_max); 
    }
  }
  else 
    printf("SUCCESS\n"); 
}

我知道构建i686-linux-musl没有错,例如,为了证明这一点,我从https://musl.cc/ x1c 0d1x下载了i686-linux-musl
我不知道为什么ldd告诉我,c是静态的,但是file告诉我它是动态链接的。
但是如果我用i686-linux-gnu(i586)

编译它
musl有什么问题?编辑。我用了strace,得到了。i686-linux-musl:

execve("./c", ["./c"], 0x7ffe6a24f548 /* 61 vars */) = 0
strace: [ Process PID=1723318 runs in 32 bit mode. ]
set_thread_area({entry_number=-1, base_addr=0xf7ff75ac, limit=0x0fffff, seg_32bit=1, contents=0, read_exec_only=0, limit_in_pages=1, seg_not_present=0, useable=1}) = 0 (entry_number=12)
set_tid_address(0xf7ff7654)             = 1723318
prlimit64(0, RLIMIT_AS, NULL, {rlim_cur=RLIM64_INFINITY, rlim_max=RLIM64_INFINITY}) = 0
prlimit64(0, RLIMIT_DATA, NULL, {rlim_cur=RLIM64_INFINITY, rlim_max=RLIM64_INFINITY}) = 0
ioctl(1, TIOCGWINSZ, {ws_row=40, ws_col=235, ws_xpixel=0, ws_ypixel=0}) = 0
writev(1, [{iov_base="ERROR MMAP - ENOMEM, RLIMIT_AS: "..., iov_len=92}, {iov_base=")\n", iov_len=2}], 2ERROR MMAP - ENOMEM, RLIMIT_AS: (4294967295:4294967295); RLIMIT_DATA: (4294967295:4294967295)
) = 94
exit_group(0)

mmap在哪里??使用i586-linux-gnu:

execve("./c2", ["./c2"], 0x7fff3e161878 /* 61 vars */) = 0
strace: [ Process PID=1723806 runs in 32 bit mode. ]
ioctl(0, TCGETS, {B38400 opost isig icanon echo ...}) = 0
ioctl(1, TCGETS, {B38400 opost isig icanon echo ...}) = 0
mmap(NULL, 2147483647, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x77f34000
write(1, "SUCCESS\n", 8SUCCESS
)                = 8
exit(8)                                 = ?
+++ exited with 8 +++
qv7cva1a

qv7cva1a1#

通过查看source code of musl's mmap,我们看到它强制大小小于PTRDIFF_MAX,可能是2147483647,如果不是,则返回ENOMEM错误。
要求少一个字节,它实际上会尝试mmap。
然而,内存碎片问题仍然存在。在32位架构上,分配2GB内存可能会成功,但不会总是成功,你必须为可能失败做好准备。许多32位操作系统将地址空间限制为最大2GB,而不是4GB,包括你分配的内存和程序本身。在这些操作系统上,你将永远无法分配2GB。我认为32位Windows和32位Linux是这样工作的。

zphenhs4

zphenhs42#

这里发生的情况是地址空间被分割了。虽然您可能仍然有足够的空闲内存和空闲地址空间用于进程,但是根本没有2GB长的未使用地址的连续范围。因此,mmap调用失败,这是应该的。当然,不同的库具有不同的分配模式,并且将在不同的地址加载(或者甚至是ASLR的随机地址),所以地址空间的最大空闲块的大小会有所不同(ASLR甚至可以故意将其设置为随机的)。
不管你喜欢与否,你必须把内存分配成更小的不连续的块,或者,如果你编译64位,你会得到一个更大的地址空间,在那里完全可以分配2GB的内存。

相关问题