引子
在linux的内存管理中,由于不同程序频繁的使用和释放内存,必然会导致内存碎片的产生。
所谓内存碎片就是内存被分割成很小很小的一些块,这些块虽然是空闲的,但是却小到无法使用。随着申请和释放次数的增加,内存将变得越来越不连续。最后,整个内存将只剩下碎片,即使有足够的空闲页框可以满足请求,但要分配一个大块的连续页框就可能无法满足,所以减少内存浪费的核心就是尽量避免产生内存碎片。
内存管理技术
为了减少和管理碎片,Linux 内存管理机制中采用了多种技术:
分页(Paging):通过将内存分为固定大小的页(通常是 4 KB),有效地减少内部碎片。
分段(Segmentation):将内存分为不同大小的段,根据进程的实际需求进行分配,可以减少外部碎片。
伙伴系统(Buddy System):一种高效的内存分配算法,通过将内存块按 2 的幂次划分和合并,减少内外部碎片。
slab 分配器:用于内核对象的内存管理,通过缓存常用对象的内存块,减少内存碎片和分配开销。
内存碎片
内部碎片
定义:内部碎片是指分配给进程的内存块比进程实际需要的内存大,导致未使用的内存空间浪费。
原因:这种情况通常发生在使用固定大小的内存块或页框进行内存分配时。例如,如果一个进程只需要 6 KB 的内存,但系统分配了一个 8 KB 的页框,那么剩余的 2 KB 内存就形成了内部碎片。
外部碎片
定义:外部碎片是指内存中的空闲空间总量虽然足够,但因为这些空闲空间是不连续的,无法满足进程一次性分配的需求。
原因:外部碎片通常出现在动态内存分配中,当进程频繁地分配和释放内存时,内存空间会被分割成许多小块。这些小块虽然总量上足够大,但由于不连续,无法分配给需要大块内存的进程。
为了解决内部碎片和外部碎片,linux的存在两种机制,即伙伴系统和slab分配器,其中本文介绍伙伴系统,另一篇介绍slab的请见本博客另一篇文章。
伙伴系统
伙伴系统介绍
伙伴系统(Buddy System)是一种高效的内存分配和管理算法,主要用于linux中的外部碎片问题。它通过将内存块划分为大小为 2 的幂次的块,并根据需求动态分配和合并内存块,从而减少内存碎片和分配开销。
主要思想
伙伴系统把所有的空闲页框分组为 11 块链表,每一块链表分别包含大小为1,2,4,8,16,32,64,128,256,512 和 1024 个连续的页框。对1024 个页框的最大请求对应着 4MB 大小的连续RAM 块。每一块的第一个页框的物理地址是该块大小的整数倍。例如,大小为 16个页框的块,其起始地址是 16 * 2^12 (2^12 = 4096,这是一个常规页的大小)的倍数。
分配内存:当需要分配内存时,找到最小的足够大的内存块进行分配。如果没有精确匹配的块存在,则找到更大的块并将其分裂成两个伙伴块,直到得到合适大小的块。
释放内存:当释放内存时,检查它的伙伴块是否也空闲。如果空闲,则将两个伙伴块合并成一个更大的块。这个过程递归进行,直到无法再合并为止。
优点
减少外部碎片:通过将内存块划分为固定大小的块,其可以有效减少外部碎片,因为内存块可以合并和分裂。
高效管理:伙伴系统使用自由链表数组来管理不同大小的块,查找和分配内存块的速度较快。
一个例子
假设我们有 1024 KB 的内存,并且需要分配 100 KB 的内存:
找到最小的足够大的块,即 128 KB 的块。
如果没有 128 KB 的块,找到更大的块(如 256 KB)并将其分裂成两个 128 KB 的块。
分配其中一个 128 KB 的块,剩下的 128 KB 块继续作为空闲块。
当释放这个 128 KB 的块时:检查它的伙伴块是否空闲。如果空闲,则将两个 128 KB 的块合并成一个 256 KB 的块。最后更新自由链表数组。
总结
Buddy System是内存管理的基础,其分配的对象的连续的物理页面,这是一个比较大的粒度,内核会在Buddy System 之上构建更加精细的内存分配机制提供给程序使用,这就是后续将要介绍的Slab。