buddy system
当系统内核初始化完毕后,使用页分配器管理物理页,Linux 使用的页分配器是伙伴分配器。
连续的物理页称为页块(page block)。 2 n 2^n 2n 个连续页称为 n 阶页块。
每阶有一个 freelist 维护该阶的空页块。
// include/linux/mmzone.h
struct zone{
...
/* free areas of different sizes */
struct free_area free_area[MAX_ORDER];
...
} zone;
分配
当需要分配一段内存时,系统会寻找合适大小的内存块。如果找到了一个恰好大小的内存块,则直接分配给请求的程序;如果没有找到,系统会向上搜索直到找到一个更大的内存块,然后将其切分成合适大小的块,并将其中一个分配给请求的程序,将剩余的部分继续保留在内存池中。
申请 128 字节的内存:
查找合适块:首先查看 size 为 128 字节的 free list,但没有空闲块。再看 256 字节的 free list,还是没有,再看 512 字节的 free list,找到一个空闲的内存块 A’。
分割内存块:将 A’划分为 256 字节的 E 和 E’,再将内存块 E 划分成 128 字节的 F 和 F’,内存块 F 即是们需要的内存。之后,在此过程中产生的 E’和 F’将分别被挂接到 size 为 256 字节和 128 字节的 free list 上,位图中 bits 的值也会相应变化。
释放
当程序释放内存时,系统会将这块内存标记为空闲状态,并尝试将其与相邻的空闲块合并,以形成更大的空闲块,方便后续的分配。
释放 128 字节的内存块 C,由于 C 相邻的两个内存块都不是空闲状态,因此不能合并,之后 C 也将被挂接到 size 为 128 字节的 free list 上。
再释放释放 64 字节的内存块 D:
分配器根据 bitmap 可知,右侧的 D’也是空闲的,且 D 和 D’的大小相同,因此 D 和 D’将合并。合并后的空闲内存块 C’为 128 字节,应该被添加到 size 为 128 字节的 free list 上,但是因为左侧的 C 也是空闲的,且 C 和 C’的大小相同,因此 C 和 C’进一步合并形成 B’,合并后的空闲内存块将被挂接到 size 为 256 字节的 free list 上。
优缺点
优点:
根据需要分配内存的大小直接可以计算出当前这次分配应该从哪个 order 的 free list 开始找空闲的内存块,因此分配的效率比 boot memory 的线性扫描 bitmap 要快很多。
缺点:
- 释放 page 的时候调用方必须记住之前该 page 分配的 order,然后释放从该 page 开始的 2 o r d e r 2^{order} 2order 个 page,对于调用者来说不方便
- 每次分配必须是 2 o r d e r 2^{order} 2order 个 page,如果需要的内存大小不是 2 的阶,会造成内部碎片。
Linux 为了解决 buddy system 造成的内部碎片问题,引入 slab 分配器处理小粒度的内存分配。
slab 分配器
Linux 中的 buddy 分配器是以 page frame 为最小粒度的,而有很多现实的应用以内核 objects 的大小来申请和释放内存的,这些内核 objects 的大小通常从几十字节到几百字节不等,远远小于一个 page 的大小。
结构
slab 分配器用于分配小块内存。它从内存管理模块申请一整块页,然后划分成多个小块的存储池,用队列来维护这些小块的状态。
在 slab 分配器中,每一类 objects 拥有一个"cache"(比如 inode_cache, dentry_cache)。每分配一个 object,都从包含若干空闲的同类 objects 的区域获取,释放时也直接回到这个区域,这样可以缓存和复用相同的 objects,加快分配和释放的速度。
cache 的内存从 buddy allocator 获得。slab 层直接面向程序的分配需求,相当于是前端,而 buddy system 则成为 slab 分配器的后端。
每个 cache 的内存在物理上是连续的。一个 cache 分成若干个 slabs,同一 cache 中的 slabs 都存储相同的 objects。
Slab 分配器是一种内存分配算法,常用于操作系统中管理内核内存。它的主要优点和缺点如下:
优缺点
优点:
- 降低内存碎片化:可以提供小块内存的分配支持,提高内存利用率。
- 减少内存分配和释放的开销:Slab 分配器通过重复使用预分配的内存块,使得内存的申请和释放不必每次需要 buddy system 介入,提高了分配和释放的效率
缺点:
- 设计实现比较复杂
- 建立 slab 分配器的数据结构就需要大量内存,可以使用经过优化的 slub 分配器
参考
Linux Memory Management - The Buddy Allocator (carcano.ch)
The Buddy System Algorithm - Linux Kernel Reference (halolinux.us)
linux/mm/mmzone.c at v2.6.39 · torvalds/linux (github.com)
内存分配[二] - Buddy系统的原理 - 知乎 (zhihu.com)