Linux Buddy Allocator

众所周知,物理内存的管理对于一个操作系统性能的重要性,那么著名的 Linux 是如何有效地管理起物理内存的呢。这里将作一个详尽的分析。

内存管理最重要的两个指标莫过于:1。减少碎片,提高利用率;2. 分配和释放的速度要快。

提到内存碎片,分为外碎片和内碎片两种。所谓的外碎片就是,当内存频繁申请和释放后,出现了很多空洞,但是它们不是连续的,当下次将要分配一块内存时,虽然空闲内存总量大于所需分配的大小,但由于这些空洞不是连续的,导致无法满足需求,这是很常见的问题;所谓内碎片就是,假设内存分配都是按照一定单位分配,比如 4K,当用户只需要 512 字节的内存大小时,由于分配了 4K 出去,导致 4K-512 的内存没有被利用,也无法再次分配,从而导致的内存的浪费。

本文的重点伙伴系统就是为解决外碎片而实现的算法。当然它的效率也是极其高的。

常规的物理内存管理算法无非利用位图或者链表来记录空闲内存块的状态,当需要分配时,扫描整条空闲链表,然后找到一块仅大于所需内存的块返回,这里仅大于是指,最好能找到一块刚好大于所以的内存块,那么就意味着,需要对空闲内存块进行排序,分配还好,每次释放时,查找合适的位置都要进行很多运算。这时自然能想到,对空闲内存进行散列归类,即按照一定规格大小分成多条链表,当进行分配和释放时,就可以直接找到相应的链表了,进而提升了效率。这样分配是提升了效率,但是释放时依旧很慢,因为内存释放时,如果相邻的内存也是空闲的,应该尽量合并成更大的内存,记录到更大内存的链表中,那么,如何能找到能合并的内存,就需要一个合适的算法了。伙伴系统就是这样一个算法。

首先说一下,Linux 管理物理内存,有一个重要的数据结构,就是 mem_map,它是一个 page 类型的数组,每个元素代表一个物理页,由 32 个字节组成,记录了所有有关该物理页框的状态。这样想知道某一页框的信息就非常的方便了。如同前面所说,伙伴系统把内存大小分类为 11 种不同规格的容量,分别为 2 的 n 次方个页框的大小,这样,当所需一块内存时,很容易就确定,从哪种规格里查找空闲页。然后待解决的就是快速查找能够合并的内存块的大小。伙伴系统规定,满足以下条件的两块内存为伙伴:

1. 两个块具有相同的大小,记作 b;

2. 它们的物理地址是连续的;

3. 第一块的第一个页框的物理地址是 (2 x b x 页大小)的倍数;

前两条规定好理解,也就是两块相同大小,并且连续的内存块才有可能是伙伴,因为这样,两块合起来就可以很顺利地添加到更上一级的空闲链表中。第三条规定了,在满足前两个条件的情况下,还需要,第一块的物理地址的限定,举个例子。

 +---------------------------------------+
 | a0 | a1 | a2 | a3 | a4 | a5 | a6 | a7 |
 +---------------------------------------+
 |   b0    |    b1   |    b2   |    b3   |
 +---------------------------------------+
 |        c0         |        c1         |
 +---------------------------------------+

假设,a0 - a7 代表物理页,那么满足条件的伙伴为 (a0, a1), (a2, a3),(a4, a5), (a6, a7); 然而 a0 和 a1 可以合并为 b0, 以此类推,(b0, b1), (b2, b3) 也是伙伴, (c0, c1) 也是伙伴。除此之外其它两两之间均不为伙伴,如 a1 和 a2 虽然满足前两个条件,但不满足第三个条件,b1 和 b2 也是,当不为伙伴的两个块即使空闲,且连续,在伙伴系统中它们也是不能合并的。这里显然,也有少许的碎片存的,但设想,其实这样的情况是极少发生的,因为如果
a0 在使用,a3 在使用,这一般发生在 a1 是在 a3 被分配之后才被释放的,虽然此时 a2 也被释放了,a1 和 a2 无法合并,但下次再分配该级别大小的内存时,就会首先找到 a1,不会再向 a3 后面的空闲内存中寻找,这样仅有这种极少的浪费,其实也是可以被接受的。

之所以会有第一种规定,因为这样,查找一个块的伙伴时就很容易定位到它的伙伴的大小,再加上第二条,就很容易知道,是向前还是向后查找它的伙伴,如果没有第三条,那两面两条也将没有意义,假设上图中, b2 块被释放时,再向前找与 b1 大小相同的块将没有意义,因为如果 b1 空闲,把 b2 和 b1 进行了合并,或者没有前两条的约束,让 b2 与 a3 进而合并,虽然可以合并到上一级中成为更大的块,由于 b2 抢了 b0 或者 a2 的伙伴,那么 b0 空闲时,就无数与其它块进行合并了,并且合并的块可能也不再规整,从而无法进行散列,慢慢就形成了碎片,所以
b2 只有唯一的伙伴 b3 ,它也只会等到 b3 被释放时,两块进行合并进而升级,这样,每一块都只将有唯一的伙伴,除非伙伴不被释放,不然它们总是可以合并。

整个结构如图:

  +-------+     +------+      +------+
  |   1   |---->|  a1  |----->|  a2  |
  +-------+     +------+      +------+
  |   2   |
  +-------+     +------+
  |   4   |-----|  c1  |
  +-------+     +------+
  |  ...  |

此时运行时的情形就很容易被分析出来了,每个级别中被挂入空闲链表的数据其实非常的少,在极端情况下,a0-a7 都被申请,但只释放了 a1, a3, a5, a7,此时才会出现空间,但在频繁的申请和释放的使用中,如果前面有空闲块,会首先被满足,所以出现空洞的实际情况比较少,当一个块被释放时,只需要简单的运算就可以确定它和它的伙伴能否合并成更大的块,以此类型,尽量地形成更大的连续空间以满足系统的需求。

只所以说合并变的简单,看一下代码就知道了。

static inline struct page *
__page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order)
{
	unsigned long buddy_idx = page_idx ^ (1 << order);

	return page + (buddy_idx - page_idx);
}

page_idx 为页框号,即 a0 之类的序号,order 为级别,即 2 的 order 次方个页框,page_idx 与 (1 << order)做异或,那么如果 page_idx 中相应的位为 1,相当于 page_idx - 2^order,因为互为伙伴的两个块的第一个块,它的地址,肯定为 (2 x b x 页大小),b 为 2 的 order 次方,因为都以页大小为单位,所以相当于 2 x 2^order,为 2 ^ (order + 1),那么它的第
2 ^ order 位肯定为 0,如果为 1 那么说明它是第二块,此时应该找第一块,所以要减去相应级别的大小,同理,如果为 0,说明它是第 1 块,此时应找第二块,所以要加上同级别的大小。

综上所述,伙伴系统的原理就很清楚了,由于有不同级别块大小的散列,使查找能满足块的空闲内存非常的迅速,再加上查找合并块的高效算法,使合并查找非常高效。这就是伙伴系统!

时间: 2024-08-03 12:45:48

Linux Buddy Allocator的相关文章

debug with Linux slub allocator

http://thinkiii.blogspot.jp/2014/02/debug-with-slub-allocator.html The slub allocator in Linux has useful debug features. Such as poisoning, readzone checking, and allocate/free traces with timestamps. It's very useful during product developing stage

linux虚拟内存子系统简介

MMU(内存管理单元) 负责将虚拟地址转换为物理地址,数据存放在主存上,cpu访存时至少需要两次,第一次获取物理地址:第二次才获取数据. TLB:为了改善虚拟地址到物理地址的转换速度,提高cpu访存速度. 原理:TLB利用的是页表的访问局部性,即当一个转换的虚拟页号被使用时,它可能在不久 的将来再次被使用到.TLB是一种高速缓存,当cpu访问第一次某个线性地址时,通 过计算获得对应的物理地址,同时,该线性地址和物理地址的对应关系保存在一个 TLB表项中,以后对同一线性地址的访问,直接从TLB表项

Linux系统调优1

Linux在进行系统调优的时候,首先要考虑整个操作系统的结构,然后针对各个部分进行优化,下面展示一个Linux系统的各个组成部分: 有上图可以看出,我们可以调整的有应用程序,库文件,内核,驱动,还有硬件本身,所以接下来讲对这些进行详细的介绍,从而是系统的性能有所提高. 内核子系统中主要包括一下几个方面: 1.         network(网络) 2.         IO(输入输出子系统) 3.         process(进程) 4.         memory(内存) 5.    

Linux系统调优

Linux在进行系统调优的时候,首先要考虑整个操作系统的结构,然后针对各个部分进行优化,下面展示一个Linux系统的各个组成部分: 有上图可以看出,我们可以调整的有应用程序,库文件,内核,驱动,还有硬件本身,所以接下来讲对这些进行详细的介绍,从而是系统的性能有所提高. 内核子系统中主要包括一下几个方面: 1.         network(网络) 2.         IO(输入输出子系统) 3.         process(进程) 4.         memory(内存) 5.    

linux内存基础知识和相关调优方案

内存是计算机中重要的部件之一,它是与CPU进行沟通的桥梁.计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大.内存作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据.只要计算机在运行中,CPU就会把需要运算的数据调到内存中进行运算,当运算完成后CPU再将结果传送出来,内存的运行也决定了计算机的稳定运行.对于整个操作系统来说,内存可能是最麻烦的的设备.而其性能的好坏直接影响着整个操作系统. 我们知道CPU是不能与硬盘打交道的,只有数据被载入到内存中才可

伙伴系统之伙伴系统概述--Linux内存管理(十四)

日期 内核版本 架构 作者 GitHub CSDN 2016-09-02 Linux-4.7 X86 & arm gatieme LinuxDeviceDrivers Linux内存管理 1 前景回顾 1.1 Linux内存管理的层次结构 Linux把物理内存划分为三个层次来管理 层次 描述 存储节点(Node) CPU被划分为多个节点(node), 内存则被分簇, 每个CPU对应一个本地物理内存, 即一个CPU-node对应一个内存簇bank,即每个内存簇被认为是一个节点 管理区(Zone)

Linux 内核源代码情景分析 chap2 存储管理(二)

几个重要的数据结构和函数 1. 物理地址管理 1.1 pgd_t, pmd_t, pte_t 页面目录PGD, 中间目录PMD 和 页面表PT 分别是由 pgd_t, pmd_t, pte_t 构成的数组, 下面给出他们的定义: ==================== include/asm-i386/page.h 36 50 ==================== 36 /* 37 * These are used to make use of C type-checking.. 38 *

【转】刚发现一个linux在线文档库。很好很强大。

原文网址:http://blog.csdn.net/longxibendi/article/details/6048231 1.网址: http://www.mjmwired.net 2.比如查看这个 proc.txt ,就在这里能找到. http://www.mjmwired.net/kernel/Documentation/filesystems/proc.txt 内核参数解释全在这里了.不过,也可以下载内核完源代码,从/usr/src/linux/Documentation/proc.tx

Linux内核情景分析的alloc_pages

NUMA结构的alloc_pages ==================== mm/numa.c 43 43 ==================== 43 #ifdef CONFIG_DISCONTIGMEM ==================== mm/numa.c 91 128 ==================== 91 /* 92 * This can be refined. Currently, tries to do round robin, instead 93 * sho