关于本网站的内存分配存在很多问题,但是我 找不到专门解决我关注的问题。 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
:
#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_memory
和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
背景:我们正试图决定如何处理我们的低内存条件
面向媒体的嵌入式应用程序,并希望了解我们是否可以 - 在此分配失败的情况下为此特定的目标信任malloc()
提醒我们。我使用桌面Linux的经验
应用程序让我觉得答案肯定是而不是,但现在我不太确定。
答案 0 :(得分:8)
malloc()
并不是说你的内核虚拟内存子系统,这是大多数现代操作系统的常见做法。当您使用malloc()
时,真正发生的事情是这样的:
malloc()
的libc实现检查其内部状态,并尝试使用各种策略优化您的请求(比如尝试使用预分配的块,分配比预先请求的内存更多的内存。 ..)。这意味着实现将影响性能并改变从内核请求的内存量,但是当检查&#34;大数字时,这并不是真正相关的,就像你在你的测试
如果预分配的内存块中没有空间(请记住,内存块通常很小,大约128KB到1MB),它会要求内核提供更多内存。实际的系统调用因内核而异(mmap()
,vm_allocate()
...)但其目的大致相同。
内核的VM子系统将处理该请求,如果它发现它是&#34;可接受的&#34; (稍后将详细介绍此主题),它将在请求任务的内存映射中创建一个新条目(我使用UNIX术语,其中task是一个包含其所有状态和线程的进程),并返回起始值所述地图条目到malloc()
。
malloc()
将记录新分配的内存块,并将为您的程序返回相应的答案。
好的,所以现在你的程序已成功 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供应商有一种疯狂的倾向,即对他们分发的系统内核进行任意更改。
在您的测试用例中,x86计算机正在按预期工作。当您以小块分配内存并且vm.overcommit_memory
设置为0时,您可以填充所有虚拟空间,即3GB线路上的某个虚拟空间,因为您需要这样做。在32位机器上运行它(如果你在64位上运行它,循环将运行直到n == N)。显然,当你尝试使用那个内存时,内核检测到物理内存变得稀缺,并激活了OOM杀手对策。
在ARM上应该是一样的。因为它没有,我想到了两种可能性:
overcommit_memory
是针对NEVER(2)政策的,可能是因为有人在内核上强制这样做了。
您已达到该任务允许的最大地图大小。
在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分割,因此系统按预期运行。