malloc是否在Linux(和其他平台)上懒惰地为分配创建后备页面?


75

在Linux上,如果我愿意malloc(1024 * 1024 * 1024),malloc实际做什么?

我确定它会为分配分配一个虚拟地址(通过遍历空闲列表并在必要时创建一个新的映射),但实际上是否创建了1 GiB的交换页?还是mprotect当您真正触摸它们时就创建地址范围并创建页面mmap

(我之所以指定Linux,是因为该标准在这些细节上没有提及,但是我想知道其他平台也可以这样做。)


2
有趣的问题;我也会对其他平台上的行为感到好奇,但是对于将这个问题锁定在Linux上感到很荣幸。
保罗·索尼尔

曾经有一段时间,这似乎让我记忆犹新……
雪橇

Answers:


43

Linux确实也推迟了页面分配。“乐观的内存分配”。从malloc取回的内存没有任何东西支持,当您触摸它时,您实际上可能会遇到OOM条件(如果请求的页面没有交换空间),在这种情况下,进程会异常终止

参见例如http://www.linuxdevcenter.com/pub/a/linux/2006/11/30/linux-out-of-memory.html


5
有趣的是,内核如何计算进程的“错误”,以找出内存不足时要杀死哪个进程。
JesperE

IIRC分为以下几层:从最高到最低-根进程,执行I / O的进程,睡眠进程...最低的项目符号。
艾登·贝尔

@Aiden链接中描述了用于确定要杀死哪个进程的“错误”功能。
亚伦·曼帕

1
较晚的OOM行为并不总是正确的。这取决于过量使用设置。有关这三种模式,请参见kernel.org/doc/Documentation/vm/overcommit-accounting
ZachB

16

9.内存(部分Linux内核Linux内核的一些言论被安德里斯·布劳威尔)是一个很好的文件。

它包含以下程序,这些程序演示Linux对物理内存和实际内存的处理方式,并说明内核的内部结构。

通常,第一个演示程序将在malloc()返回NULL之前获得大量内存。既然实际使用了较早获得的内存,第二个演示程序将获得少得多的内存。第三个程序将获得与第一个程序相同的数量,然后在要使用其内存时将其杀死。

演示程序1:不使用就分配内存。

演示程序2:分配内存并实际触摸所有内容。

演示程序3:先分配,然后再使用。

(在运行良好的系统(如Solaris)上,这三个演示程序获得相同数量的内存,并且不会崩溃,但请参见malloc()返回NULL。)


6
“运作良好”是一个意见问题。实际上,Linux在/ proc / sys / vm中具有选项来控制过量使用行为。如果愿意,您可以像Solaris一样拥有它。
Zan Lynx

2
警告,/ proc / sys / vm大多数时候都被破坏了!!groups.google.com/group/comp.os.linux.development.system/… 这是Linux和磁盘性能的一个很好的提示。如果您曾经做过大拷贝,并且大量缓存已用完,并且您的I / O系统开始陷入困境.... echo 1> / proc / sys / vm / drop_caches,然后将您的备份恢复为-高通量:)去图吧!
RandomNickName42

11

我将此答案提供给有关同一主题的类似帖子:

有些分配器是懒惰的吗?

这从一个主题开始(然后我将其与您的问题联系在一起),但是发生的事情类似于在Linux中派生一个进程时发生的事情。在分叉时,有一种称为写时复制的机制,该机制仅在也写入内存时才为新进程复制内存空间。这样,如果分叉的流程执行程序立即是一个新程序,那么您就节省了复制原始程序内存的开销。

回到您的问题,这个想法是相似的。正如其他人指出的那样,请求内存会立即为您提供虚拟内存空间,但是实际页面仅在写入它们时才分配。

目的是什么?它基本上使对内存的分配成为一个或多或少的恒定时间操作Big O(1)而不是Big O(n)操作(类似于Linux调度程序进行工作分配的方式,而不是大批量地执行)。

为了证明我的意思,我做了以下实验:

rbarnes@rbarnes-desktop:~/test_code$ time ./bigmalloc

real    0m0.005s
user    0m0.000s
sys 0m0.004s
rbarnes@rbarnes-desktop:~/test_code$ time ./deadbeef

real    0m0.558s
user    0m0.000s
sys 0m0.492s
rbarnes@rbarnes-desktop:~/test_code$ time ./justwrites

real    0m0.006s
user    0m0.000s
sys 0m0.008s

bigmalloc程序分配2000万个整数,但不对其进行任何处理。deadbeef向每个页面写入一个int,导致19531次写入,justwrites分配19531个int,并将它们清零。如您所见,deadbeef的执行时间比bigmalloc大约长100倍,比justwrites大约长50倍。


6

Malloc从libc管理的块中分配内存。当需要更多内存时,该库将使用brk系统调用进入内核。

内核将虚拟内存页面分配给调用进程。页面作为流程拥有的资源的一部分进行管理。内存不足时不分配物理页。当进程访问brk'd页面之一中的任何内存位置时,将发生页面错误。内核验证虚拟内存已分配,然后继续将物理页面映射到虚拟页面。

页面分配不仅限于写操作,而且与写时复制完全不同。任何访问,读取或写入都会导致页面错误和物理页面映射。

请注意,堆栈内存是自动映射的。也就是说,不需要显式的brk来将页面映射到堆栈使用的虚拟内存。


请注意,glibc通过映射匿名页面而不是使用brk来满足大量分配。参见gnu.org/software/libc/manual/html_node/The-GNU-Allocator.html
ZachB

5

在Windows上,页面已提交(即,可用内存减少了),但是直到您触摸页面(读或写)后,它们才真正分配。


2

在大多数类Unix系统上,它管理brk边界。VM被处理器命中时会添加页面。至少Linux和BSD执行此操作。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.