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

2024-04-21

这个网站上有很多关于内存分配的问题,但是我 找不到专门解决我的问题的人。这 问题 https://stackoverflow.com/questions/19148296/linux-memory-overcommit-details似乎最接近,它引导我这 文章 http://www.win.tue.nl/~aeb/linux/lk/lk-9.html#ss9.6,所以...我比较了 它包含的三个演示程序在(虚拟)桌面 x86 上的行为 Linux系统和基于ARM的系统。

我的发现很详细here http://evadeflow.com/2013/11/overcommit-and-oom-on-embedded-linux/, 但 快速总结是:在我的桌面系统上,demo3节目从 文章似乎表明malloc() always关于内存量的谎言 已分配——即使禁用了交换。例如,它高兴地“分配”3 GB RAM,然后在程序开始实际运行时调用 OOM 杀手 写入所有内存。禁用交换后,OOM 杀手将被调用 仅写入 3 GB 中的 610 MB 后malloc()据说已经做了 可用的。

The purpose演示程序的主要目的是演示 Linux 的这一有据可查的“功能”,因此这一切都不足为奇。 但在我们基于 i.MX6 的嵌入式目标上,行为有所不同, 在哪里malloc()似乎说的是关于它有多少内存的真相 allocates(?) 下面的程序(从文章中逐字复制)总是 在第二个循环中被 OOM 杀死i == n:

#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;
}

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

NOTE:每个系统的一些详细信息(请注意overcommit_memory and overcommit_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

背景:我们正在尝试决定如何处理我们的内存不足的情况 面向媒体的嵌入式应用程序,并想知道我们是否可以为此specific目标——信任malloc()当分配失败时提醒我们。我的桌面 Linux 体验 应用程序让我认为答案是肯定的not,但现在我不太确定。


一点背景

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

  1. libc 的实现malloc()检查其内部状态,并尝试使用各种策略来优化您的请求(例如尝试使用预分配的块、分配比提前请求更多的内存......)。这意味着该实现将影响性能并稍微改变内核请求的内存量,但这在检查“大数字”时并不真正相关,就像您在测试中所做的那样。

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

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

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

好的,现在你的程序已经成功了已分配内存一些记忆,但事实是物理内存的单页(x86 中为 4KB)尚未实际分配给您的请求(嗯,这是一个过于简单化的问题,因为一些页面可能被用来存储有关内存池状态的信息,但它更容易说明这一点)。

那么,当您最近尝试访问此内容时会发生什么已分配内存记忆?分段错误。令人惊讶的是,这是一个相对鲜为人知的事实,但您的系统正在生成分段错误每时每刻。然后,您的程序被中断,内核接管控制权,检查地址错误是否对应于有效的映射条目,获取一个或多个物理页并将它们链接到任务的映射。

如果您的程序尝试访问不在任务中的映射条目内的地址,则内核将无法解决该错误,并将向其发送信号(或非 UNIX 系统的等效机制)以指出这个问题。如果程序本身不处理该信号,它将被臭名昭著的命令杀死分段故障 error.

So 调用时没有分配物理内存malloc(),但是当你实际访问该内存时。这允许操作系统执行一些漂亮的技巧,例如磁盘paging, 气球 and 过度投入.

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

  • 虚拟尺寸:已请求的内存量,即使实际上并未使用。

  • 居民规模:它真正使用的内存,由物理页支持。

过量使用多少就足够了?

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

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

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

事实并非如此。至少不应该如此,但 ARM 供应商有一种疯狂的倾向,会对他们随系统分发的内核进行任意更改。

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

在ARM上应该是一样的。由于事实并非如此,我想到了两种可能性:

  1. overcommit_memory采用 NEVER (2) 策略,可能是因为有人在内核上强制采用这种方式。

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

与 ARM 上的每次运行一样,您会得到不同的值malloc阶段,我会放弃第二个选项。确保overcommit_memory已启用(值 0)并重新运行测试。如果您有权访问这些内核源代码,请查看它们以确保内核尊重这一点sysctl(正如我所说,一些 ARM 供应商喜欢对他们的内核做一些令人讨厌的事情)。

作为参考,我在 QEMU 下模拟 vertilepb 和 Efika MX (iMX.515) 上运行了 demo3。第一个停了内存分配3 GB 标记,正如 32 位机器上所预期的那样,而另一个则更早,为 2 GB。这可能会让人感到惊讶,但如果你看一下它的内核配置(https://github.com/genesi/linux-legacy/blob/master/arch/arm/configs/mx51_efikamx_defconfig 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 分割,因此系统的行为符合预期。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Linux malloc() 在 ARM 和 x86 上的行为是否不同? 的相关文章

随机推荐