Linux malloc()在ARM vs x86上的行为是否有所不同?

时间:2013-11-08 21:12:05

标签: linux embedded arm malloc

关于本网站的内存分配存在很多问题,但是我 找不到专门解决我关注的问题。 This question 似乎最接近,它导致我this article,所以...我比较了 它包含在(虚拟)桌面x86上的三个演示程序的行为 Linux系统和基于ARM的系统。

我的发现详细here,但是 快速摘要是:在我的桌面系统上,来自的demo3程序 文章似乎表明malloc() 总是与内存量有关 已分配 - 甚至禁用交换。例如,它愉快地'分配'3 GB的RAM,然后在程序实际开始时调用OOM杀手 写给所有那些记忆。禁用交换后,将调用OOM杀手 在写入只有610 MB的3 GB malloc()之后 可用。

演示程序的目的是为了演示这个记录良好的Linux“功能”,所以这一点都不太令人惊讶。 但是我们基于i.MX6的嵌入式目标在工作中的行为是不同的, 其中malloc()似乎在说明它有多少RAM 分配(?)以下程序(从文章逐字复制)总是如此 在i == n

时,在第二个循环中被OOM杀死
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define N       10000

int main (void) {
        int i, n = 0;
        char *pp[N];

        for (n = 0; n < N; n++) {
                pp[n] = malloc(1<<20);
                if (pp[n] == NULL)
                        break;
        }
        printf("malloc failure after %d MiB\n", n);

        for (i = 0; i < n; i++) {
                memset (pp[i], 0, (1<<20));
                printf("%d\n", i+1);
        }

        return 0;
}

所以我的问题,简而言之,:为什么demo3程序 - 或其他一些 不幸的OOM杀手受害者 - 总是i == n之前很久就被杀死了 桌面系统(暗示malloc()是骗子),但它只会被杀死 在我们的i.MX6 ARM目标上i == n时(意味着malloc()可能会告诉您 真相)?这个差异是libc和/或内核版本的函数,还是 别的什么?我可以得出结论:malloc() 总是返回NULL 分配在这个目标上失败了?

注意:每个系统的一些详细信息(请注意,overcommit_memoryovercommit_ratio的值都相同):

# Desktop system
% uname -a
Linux ubuntu 3.8.0-33-generic #48-Ubuntu SMP Wed Oct 23 17:26:34 UTC 2013 i686 i686 i686 GNU/Linux
% /lib/i386-linux-gnu/libc.so.6 
GNU C Library (Ubuntu EGLIBC 2.17-0ubuntu5.1) stable release version 2.17, by Roland McGrath et al.
Copyright (C) 2012 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.
Compiled by GNU CC version 4.7.3.
Compiled on a Linux 3.8.13 system on 2013-09-30.
Available extensions:
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<https://bugs.launchpad.net/ubuntu/+source/eglibc/+bugs>.
% cat /proc/sys/vm/overcommit_memory
0
% cat /proc/sys/vm/overcommit_ratio 
50

# i.MX6 ARM system
# uname -a
Linux acmewidgets 3.0.35-ts-armv7l #2 SMP PREEMPT Mon Aug 12 19:27:25 CST 2013 armv7l GNU/Linux
# /lib/libc.so.6
GNU C Library (GNU libc) stable release version 2.17, by Roland McGrath et al.
Copyright (C) 2012 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.
Compiled by GNU CC version 4.7.3.
Compiled on a Linux 3.0.35 system on 2013-08-14.
Available extensions:
    crypt add-on version 2.1 by Michael Glad and others
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
libc ABIs: UNIQUE
For bug reporting instructions, please see:
<http://www.gnu.org/software/libc/bugs.html>.
# cat /proc/sys/vm/overcommit_memory
0
% cat /proc/sys/vm/overcommit_ratio 
50

背景:我们正试图决定如何处理我们的低内存条件 面向媒体的嵌入式应用程序,并希望了解我们是否可以 - 在此分配失败的情况下为此特定的目标信任malloc()提醒我们。我使用桌面Linux的经验 应用程序让我觉得答案肯定是而不是,但现在我不太确定。

1 个答案:

答案 0 :(得分:8)

一点背景

malloc()并不是说你的内核虚拟内存子系统,这是大多数现代操作系统的常见做法。当您使用malloc()时,真正发生的事情是这样的:

  1. malloc()的libc实现检查其内部状态,并尝试使用各种策略优化您的请求(比如尝试使用预分配的块,分配比预先请求的内存更多的内存。 ..)。这意味着实现将影响性能并改变从内核请求的内存量,但是当检查&#34;大数字时,这并不是真正相关的,就像你在你的测试

  2. 如果预分配的内存块中没有空间(请记住,内存块通常很小,大约128KB到1MB),它会要求内核提供更多内存。实际的系统调用因内核而异(mmap()vm_allocate() ...)但其目的大致相同。

  3. 内核的VM子系统将处理该请求,如果它发现它是&#34;可接受的&#34; (稍后将详细介绍此主题),它将在请求任务的内存映射中创建一个新条目(我使用UNIX术语,其中task是一个包含其所有状态和线程的进程),并返回起始值所述地图条目到malloc()

  4. malloc()将记录新分配的内存块,并将为您的程序返回相应的答案。

  5. 好的,所以现在你的程序已成功 malloc&#39; 一些内存,但事实是不是单个页面(x86中为4KB)的物理内存实际上已经分配给你的请求了(嗯,这是一个过于简单化的,因为有些页面可能已被用来存储有关内存池状态的信息,但它更容易说明这一点)。

    那么,当您尝试访问最近 malloc&#39; ed 内存时会发生什么? 分段错误。令人惊讶的是,这是一个相对鲜为人知的事实,但您的系统始终会产生的分段错误。然后中断您的程序,内核获得控制权,检查地址错误是否对应于有效的映射条目,获取一个或多个物理页面并将它们链接到任务的映射。

    如果您的程序试图访问不在任务中的映射条目内的地址,则内核将无法解决该错误,并将信号(或非UNIX系统的等效机制)发送到它指出了这个问题。如果程序本身没有处理该信号,它将被臭名昭着的 Segmentation Fault 错误杀死。

    因此,当您呼叫malloc()时,物理内存未分配,但实际访问该内存时。这允许操作系统做一些漂亮的技巧,如磁盘分页 balloning 过度使用

    这样,当您询问特定进程使用了​​多少内存时,您需要查看两个不同的数字:

    • 虚拟大小:已请求的内存量,即使它未实际使用过。

    • Resident Size :实际使用的内存,以物理页面为后盾。

    多少过度使用就足够了?

    在计算中,复杂问题中的资源管理。您有各种各样的策略,从最严格的基于功能的系统到Linux等内核(带memory_overcommit == 0)的更轻松的行为,它基本上允许您请求内存到最大地图大小允许任务(这是一个取决于架构的限制)。

    在中间,你有像Solaris这样的操作系统(在你的文章中提到),它将任务的虚拟内存量限制为(physical pages + swap disk pages)的总和。但是不要被你引用的文章所迷惑,这并不总是一个好主意。如果您正在运行Samba或Apache服务器,同时运行数百到数千个独立进程(由于碎片导致大量虚拟内存浪费),您将不得不配置一个荒谬的数量交换磁盘,或者你的系统将耗尽虚拟内存,同时仍有大量的可用内存。

    但为什么内存过量使用在ARM上的工作方式不同?

    它没有。至少它不应该,但ARM供应商有一种疯狂的倾向,即对他们分发的系统内核进行任意更改。

    在您的测试用例中,x86计算机正在按预期工作。当您以小块分配内存并且vm.overcommit_memory设置为0时,您可以填充所有虚拟空间,即3GB线路上的某个虚拟空间,因为您需要这样做。在32位机器上运行它(如果你在64位上运行它,循环将运行直到n == N)。显然,当你尝试使用那个内存时,内核检测到物理内存变得稀缺,并激活了OOM杀手对策。

    在ARM上应该是一样的。因为它没有,我想到了两种可能性:

    1. overcommit_memory是针对NEVER(2)政策的,可能是因为有人在内核上强制这样做了。

    2. 您已达到该任务允许的最大地图大小。

    3. 在ARM上的每次运行中,您获得 malloc 阶段的不同值,我会丢弃第二个选项。确保overcommit_memory已启用(值为0)并重新运行测试。如果您可以访问这些内核源代码,请查看它们以确保内核尊重此 sysctl (正如我所说,某些ARM供应商喜欢对其内核执行令人讨厌的事情)。

      作为参考,我在QEMU模拟vertilepb和Efika MX(iMX.515)下运行了demo3。第一个在3 GB标记处停止了 malloc&#39>,正如在32位计算机上所预期的那样,而另一个在早期以2 GB的速度运行。这可能会让人感到意外,但是如果你看一下它的内核配置(https://github.com/genesi/linux-legacy/blob/master/arch/arm/configs/mx51_efikamx_defconfig),你会看到这个:

      CONFIG_VMSPLIT_2G=y
      # CONFIG_VMSPLIT_1G is not set
      CONFIG_PAGE_OFFSET=0x80000000
      

      内核配置了2GB / 2GB分割,因此系统按预期运行。