目录


buddy 分配器原理

Buddy 内存管理器用于管理 Linux 内核中可用的物理内存。在 Linux 内核中,将 物理内存以 PAGE_SIZE 为单位,划分成大量的内存区域,这些内存区域称为物理页, 或称为 “Page”。Linux 将每个物理页与物理地址进行关联进行编号,每个物理页就有 一个唯一的 ID,这个 ID 称为物理页帧号,或称为 “Page Fram”。在不同的体系结构 中,物理内存的物理地址不一定都是从地址 0 开始,因此物理页帧不一定从 0 开始 编号。物理页、物理页帧与物理内存之间的关系如下图:

正如上图所示,物理内存的起始地址是 PHYS_OFFSET, 物理内存以 PAGE_SIZE 为单位 被划分许多内存区域 “Memory Region”, 将这些内存区域按物理地址从低到高一次 标号,因此就形成了物理页帧号 “Page Frame”. 由于 PHYS_OFFSET 在不同的体系结构 中数值不一样,但可以得到其之间的关系:

Region0 起始地址 = PHYS_OFFSET + Fram0 * PAGE_SIZE
                 = PHYS_OFFSET + 0 * PAGE_SIZE
                 = PHYS_OFFSET
                 = ((PHYS_OFFSET >> PAGE_SHIFT) << PAGE_SHIFT) + (((0 * PAGE_SIZE) >> PAGE_SHIFT) << PAGE_SHIFT)
                 = (PHYS_OFFSET >> PAGE_SHIFT + 0) << PAGE_SHIFT
Region1 起始地址 = PHYS_OFFSET + Fram1 * PAGE_SIZE
                 = PHYS_OFFSET + 1 * PAGE_SIZE
                 = ((PHYS_OFFSET >> PAGE_SHIFT) << PAGE_SHIFT) + (((Fram1 * PAGE_SIZE) >> PAGE_SHIFT) << PAGE_SHIFT)
                 = ((PHYS_OFFSET >> PAGE_SHIFT + ((Fram1 * PAGE_SIZE) >> PAGE_SHIFT))) << PAGE_SHIFT
                 = (PHYS_OFFSET >> PAGE_SHIFT + Fram1) << PAGE_SHIFT
Region2 起始地址 = PHYS_OFFSET + Fram2 * PAGE_SIZE
                 = PHYS_OFFSET + 2 * PAGE_SIZE
                 = ((PHYS_OFFSET >> PAGE_SHIFT) << PAGE_SHIFT) + (((Fram2 * PAGE_SIZE) >> PAGE_SHIFT) << PAGE_SHIFT)
                 = ((PHYS_OFFSET >> PAGE_SHIFT + ((Fram2 * PAGE_SIZE) >> PAGE_SHIFT))) << PAGE_SHIFT
                 = (PHYS_OFFSET >> PAGE_SHIFT + Fram2) << PAGE_SHIFT

从上面的推到关系可以反推得到:

物理地址 = PHYS_OFFSET + 物理页帧号 << PAGE_SHIFT
PhysAddr = PHYS_OFFSET + PAGE_Frame << PAGE_SHIFT
物理页帧号 = 物理地址 >> PAGE_SHIFT
PAGE_Frame = PhysAddr >> PAGE_SHIFT

经过上面的讨论,已经明确了物理地址和物理页帧之间的关系,Linux 习惯称物理 地址为 “PA”、”phys” 或者 “pa”,称物理页帧为 “PFN” 或 pfn,称物理页为 “page”, 内核提供了多个三者之间的转换函数:

page = pfn_to_page(pfn)
page = phys_to_page(pa)
pfn  = page_to_pfn(page)
pfn  = phys_to_pfn(pa)
pa   = page_to_phys(page)
pa   = pfn_to_phys

Linux 内核将物理内存通过物理页,物理页帧和物理地址定义管理之后,又以物理 页为单位,根据物理页的用途,将物理页划分到不同的区间,Linux 通常使用 Zone 来表示区间。一个 Zone 里面包含多个物理页,Zone 定义了这些物理页的用途范围。 Linux 目前支持的 Zone 类型有 ZONE_DMA、ZONE_DMA32、ZONE_NORMAL 和 ZONE_HIGHMEM. 内核使用 struct zone 来管理一个分区里面的所有物理页的使用, 包括当前分区物理页数量、可用物理页数量、已经分配物理页数量等信息。Liux Zone 划分如下图:

不同的架构 ZONE 的划分不一样,但基本已上图类似。在上图中定义了 4 个 Zone, 其中 ZONE_DMA 和 ZONE_DMA32 位于物理内存的最前端 (但不是一定位于最前端), 主要是给一些 ISA 总线等总线上 I/O 设备使用的物理内存, 或者是给架构中 DMA 直接使用的物理内存,也可作为普通物理内存使用. ZONE_NORMAL 区间内的物理页 用于内核日常运行所使用的普通物理内存,多数架构中 ZONE_NORMAL 区间内存的 物理内存都与虚拟地址进行一一映射,即线性映射,但值得注意的是映射和物理内存 是相互独立又相互关联的存在。ZONE_HIGMEM 区间是在一些架构中存在,主要是由于 虚拟内存不够,不能将物理内存与虚拟内存一一映射的这类物理页,统一放到了 ZONE_HIGHMEM 区间内,Linux 内核的 VMALLOC,KMAP 和 FIXMAP 等一些内存分配 器会将 ZONE_HIGHMEM 区间内的物理页通过一定的逻辑关系映射到虚拟地址,以供 进程(内核进程、内核线程或用户进程)使用。Linux 内核使用 struct zone 管理 一个 ZONE 分区。

Buddy 分配器初始化

有了上面的基础数据介绍之后,那么开始讨论今天的主角 Buddy 物理内存管理器。 Buddy 物理内存管理器用于管理 Zone 区间内的物理页,包括物理页的申请、释放 和回收。正如上图所示,struct zone 管理一个 ZONE 分区,其中 free_area 成员一个链表集合,总共包含 MAX_ORDER 个链表。每个链表用于维护一定的物理页块, 这里的物理页块指的是一个或多个物理页的集合。在 struct zone 结构中, free_area[0] 维护的物理页数为 1 的物理页块; free_area[1] 维护物理页数 2 的物理页块; free_area[2] 维护物理页数为 4 的物理页块. 以此类推,free_area[n] 维护物理页数为 2^n 的物理页块.

Linux 内核初始化过程中,bootmem/MEMBLOCK 内存分配器初始化完物理内存之后, 使用循环将可以物理内存按一定的长度转移给 Buddy 内存分配器。这里值得注意 的是 bootmem 分配器和 MEMBLOCK 分配器每次转移的物理内存块长度不同。对于 bootmem 分配器转移物理块的最大长度是 BITS_PER_LONG 个物理页集合,最小长度 是一个物理页; MEMBLOCK 分配器转移的物理块的最大长度是 2^MAX_ORDER 个物理 页集合,最小长度是一个物理页。无论是 bootmem 分配器还是 MEMBLOCK 分配器, Buddy 内存管理器在介绍到物理页块之后,按最大 2 的幂次个物理页对物理页块 进行拆分,正如上图所示,被才分成三个块,每个块都包含 2 的倍数个物理页。

Buddy 内存分配器将拆分只有的物理页块依次插入到指定的 free_area[] 链表中, 在每次插入过程中,Buddy 内存分配器先找到物理页块对应的 free_area[] 链表.

Buddy 内存管理器在指定的 free_area[] 链表中遍历所有的成员,查找是否与 即将插入的物理页块存在 “Buddy” 关系,何为 Buddy 关系呢? 正如上图所示, 例如在物理页集合 PageSet0 中,其包含 2^N 个物理页,在这个物理页集合中, 第一个物理页的页帧号为 PFN0, 那么其 “Buddy” 物理页帧号的关系如下:

PFN_Buddy  = PFN0 ^ (1 << N)

N 代表物理页块的幂次,通过上面的公式就可以获得一个 PageSet 的 “Buddy” PageSet 的页帧号,如上图所示,PageSet0 和 PageSet1 是 “Buddy”; PageSet0 与 PageSet1 组成的物理块和 PageSet2 与 PageSet3 组成的物理块是 “Buddy”; PageSet0、PageSet1、PageSet2、PageSet3 组成的物理页块与 PageSet4、PageSet5、 PageSet6、PageSet7 组成的物理页块是 “Buddy” 关系。

当在 free_area[] 指定的链表中遍历所有的成员,并未发现存在插入物理页块的 “Buddy” 物理页块,那么 Buddy 就按上图所示的插入到 free_area[] 指定链表的 尾部。

当 Buddy 管理器遍历 Free_area[] 指定链表的所有成员时,找到了即将插入物理页块 的 “Buddy” 时,Buddy 管理器继续检测找到的 “Buddy” 物理页块是否满足条件进行 合并,如果满足合并条件,那么 Buddy 管理器就将 “Buddy” 物理页块从指定的 free_area[] 链表中移除,并与插入的物理页块合成一个更大的物理页块。Buddy 内存分配器继续重复上面的逻辑,继续在下一级 free_area[] 链表中查找 “Buddy” 物理页块,直到找不到 “Buddy” 物理页块或者已经到达 free_area[] 最后一个链表, 那么 Buddy 内存分配器就将物理页块插入到指定的 free_area[] 链表中。

Buddy 内存管理器将 bootmem/MEMBLOCK 分配器转移的物理页块全部加入到 free_area[] 链表之后,bootmem/MEMBLOCK 分配器任务就完成,内核将弃用 bootmem/MEMBLOCK 内存分配器,之后 Buddy 内存分配器将管理可用的物理内存,至此 Buddy 内存分配器 初始化完毕. 从上面的讨论可以知道,每个 ZONE 分区都维护自己的 free_area[] 链表集合,那么对于单 node 的系统,系统中有多少个 ZONE 分区,系统就有多少个 独立的 “Buddy 内存分配器”。

那么多个独立 Buddy 分配器之间存在怎样的联系呢? 当从指定 ZONE 上分配物理内存 的时候,如果当前 ZONE 有可用的物理内存,那么 Buddy 就会从指定 ZONE 对应的 “Buddy 分配器” 上分配可用物理内存; 如果当前 ZONE 上没有可用物理内存,那么 Buddy 就按一定的顺序从其他 Zone 上获得可用物理内存,关系如下:

从上图不免可以得到 Buddy 分配的关系:

1. 当从 ZONE_HIGHMEM 区间上分配可用物理内存,如果 ZONE_HIGHMEM 没有可用物理
   物理内存,那么 Buddy 继续从 ZONE_NORAML 中分配物理内存; 如果 ZONE_NORMAL
   分区中没有可用物理内存,那么从 ZONE_DMA32 中分配物理内存; 如果 ZONE_DMA32
   分区中没有可用物理内存,那么从 ZONE_DMA 中分配物理内存; 如果 ZONE_DMA 分
   区中没有可用物理内存,那么 OOM.
2. 当从 ZONE_NORMAL 区间上分配可用物理内存,如果 ZONE_NORMAL 分区中没有可用
   物理内存,那么从 ZONE_DMA32 中分配物理内存; 如果 ZONE_DMA32 分区中没有可
   用物理内存,那么从 ZONE_DMA 中分配物理内存; 如果 ZONE_DMA 分区中没有可用
   物理内存,那么 OOM.
3. 当从 ZONE_DMA32 区间上分配可用物理内存, 如果 ZONE_DMA32 分区中没有可用物
   理内存,那么从 ZONE_DMA 中分配物理内存; 如果 ZONE_DMA 分区中没有可用物理
   内存,那么 OOM.
4. 当从 ZONE_DMA 区间上分配可用物理内存, 如果 ZONE_DMA 分区中没有可用物理内
   存,那么 OOM.

从上面分配关系就可以知道为什么有的区间物理内存很珍贵,而有的物理内存很廉价.

Buddy 分配器分配

Buddy 分配器以物理页为单位为内核其他子系统提供内存的分配申请。Buddy 分配器 提供了多个便捷的函数 API 以便其他子系统进行物理内存分配。

static inline struct page * alloc_pages(gfp_t gfp_mask, unsigned int order)

例如使用上面的函数进行物理内存分配的时候,需要提供分配标志以及分配大小。 分配标志是用于表明分配的目的和分配的紧急缓慢,标志定义在 “linux/gfp.h”:

GFP_DMA
  作用: 从 ZONE_DMA 内分配内存
GFP_DMA32
  作用: 优先从 ZONE_DMA32 中分配内存,未找到可以到 ZONE_DMA 中分配物理内存
GFP_KERNEL
  作用: 优先从 ZONE_KERNEL 中分配物理内存,未找到可以依次从 ZONE_DMA32 到
        ZONE_DMA 分区中进行查找.
__GFP_HIGHMEM
  作用: 优先从 ZONE_HIGHMEM 中分配物理内存,未找到可以依次从 ZONE_NORMAL
        到 ZONE_DMA32, 最后到 ZONE_DMA 分区中查找可用物理内存.
GFP_ATOMIC
  作用: 分配物理内存不能等待或休眠,如果没有可用物理内存,直接返回。
__GFP_WAIT
  作用: 如果未找到可用物理内存,可以进入休眠等待 Kswap 查找可用物理内存.
__GFP_ZERO
  作用: 分配的物理内存必须被清零.
__GFP_HIGH
  作用: 当系统可用物理很紧缺的情况下,可以从紧急内存池中分配物理内存.
__GFP_NOFAIL:
  作用: 当系统中没有可用物理内存的情况下,可以进入睡眠等待 kswap 交换出更
        多的可用物理内存, 或者等待其他进程释放物理内存,直到分配到可用物理
        内存.
__GFP_COLD:
  作用: 从 ZONE 分区的 PCP 冷页表中获得可用物理页.
__GFP_REPEAT:
  作用: 当 Buddy 分配器没有可用物理内存时,进行 retry 操作以此分配到可用物
        理内存,但由于 retry 的次数有限,可能会失败.
__GFP_NORETRY:
  作用: 当 Buddy 分配器没有可用物理内存时,不进行 retry 操作,直接反馈结果。 

在上面的例子中,使用 alloc_pages() 函数从 Buddy 分配器中分配物理内存,Buddy 分配器首先通过 GFP 标志判断从哪个 ZONE 中分配物理内存,确认完毕之后再通过 参数确定需要分配页的大小。综合上面的信息之后,Buddy 内存分配器就从指定的 ZONE 的 free_area[] 链表中查找可用的物理页块。这时会涉及两种基本情况:

例如上面的情况,需要从指定的 ZONE 中分配 1 个物理页,但由于此时该 ZONE 的 free_area[0] 里面中没有可用的物理页块,那么 Buddy 分配器就会从下一级 free_area[1] 链表中查找一个可用的物理页,如果找到将其从 free_area[2] 链 表中移除,然后对半拆分成两个物理页块,一个给分配物理内存的请求者,另外 一个物理页块插入到低一级的 free_area[0] 里面, 如下图是分配之后的情况:

在第一种情况下,如果向高一级的 free_area[] 查找可用物理页没有找到,那么 再向更高一级 free_area[] 查找,直到查找到 free_area[MAX_ORDER-1] 链表为止。 如果在更高的 free_area[] 链表中查找到可用物理页块,那么 Buddy 分配器就会 将可用物理页块从当前的 free_area[] 中移除,然后从中间分成两块相等的物理 页块,一块插入低一级的 free_area[] 链表中,另外一块如果比需求的物理页块 还大,那么继续从中间均分成两块,一块插入到更低一级 free_area[] 链表中。 重复上面的动作知道分到合适的物理页块.

对于第二种情况,Buddy 分配器从指定的 ZONE 上分配指定大小的物理内存,正好 对于的 free_area[] 链表上正好存在可以物理页块,那么 Buddy 就直接从指定的 free_area[] 上移除可用物理页块给申请者即可。

以上两种情况均是 Buddy 分配器的通常遇到的情况,但由于运行时间很长,系统中 存在某些长度的物理页块已经很紧缺,甚至已经没有,这个 Buddy 内存分配器往往 采取一些测量来应对这些问题。

第一种情况就是当前 ZONE 已经没有可用的物理页块了,那么就按上面的关系从其他 ZONE 中进行查找. 如果找到就反馈给调用者,如果没有找到,那么就进行第二种情况。

第二种情况当前可用物理页数量已经低于 watermark, 那么 Buddy 内存分配器会唤 醒 Kswap 内核线程,Kswap 线程会查找各个文件系统中 LRU Page 链表情况,通过 一定的算法确认哪些 Page 可以换页到磁盘上。换页完成之后会释放出更多的可用物 理页,那么 Buddy 确认是否满足申请,如果满足,那么将可用的物理页传递给申请 者。如果不满足那么 Buddy 进入第三种情况。

第三种情况是 Buddy 内存分配器就会执行 try_to_free_pages() 操作,该操作会 在内核中尽可能查找可用的物理内存,尽量释放可以释放的物理内存,以便满足 申请需求,这样的操作如果不能满足申请需求,那么 Buddy 内存分配器将会触发 OOM。

Buddy 分配器释放

当使用完物理内存之后,可使用 Buddy 内存分配器提供的函数将物理内存返回 给 Buddy 内存分配器,如下:

fastcall void __free_pages(struct page *page, unsigned int order)

调用者只需将指向 page 的指针和物理页大小的信息传递给 Buddy 分配器, Buddy 分配器获得这些信息之后首先确认 page 属于哪个 ZONE 分区,然后根据 order 确认插入到哪个 free_area[] 链表里。当插入的过程中,Buddy 分配器 会查找 page 的 “Buddy” 是否正在指定的 free_area[] 链表中,如果 “Buddy” 物理页块正好存在,而且可用,那么 Buddy 分配器将 page 与 “Buddy” page 进行合并成更大的物理页块,然后 Buddy 分配器再向高一级的 free_area[] 链表中查找新物理页块的 “Buddy” 物理页块,同理如果 “Buddy” 可用,那么继续 合并更大的物理页块,再循环向更高一级的 free_area[] 中合并,直到合并到 free_area[MAX_ORDER-1] 为止。如果 page 找不到 “Buddy” page, 那么 Buddy 分配器就将 page 插入到指定的 free_area[] 链表即可.


buddy 的优点

Buddy 内存分配器用于分配物理内存,也只管理物理内存,并不涉及虚拟内存 的管理。系统可以从 Buddy 内存分配器中分配连续的物理页块。

buddy 的缺点

由于系统长时间的运行,Buddy 内存分配器中大块的物理页块越来越少,对 大块连续物理内存的申请带来不便.


buddy 分配器使用


基础用法介绍

Buddy 内存分配器相关的源文件位于:

mm/page_alloc.c include/linux/gfp.h

Buddy 内存分配器提供了多个 API 以便物理内存的分配是释放:

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:


ZONE_DMA 中分配物理页

从 ZONE_DMA 中分配物理内存,开发者可以参考如下代码:

#include <linux/gfp.h>
#include <linux/mm.h>

static int TestCase_alloc_page_from_DMA(void)
{
        struct page_bs *pages;
        int order = 2;
        void *addr;

        /* allocate page from Buddy (DMA) */
        pages = alloc_pages(GFP_DMA, order);
        if (!pages) {
                printk("%s error\n", __func__);
                return -ENOMEM;
        }

        /* Obtain page virtual address */
        addr = page_address(pages);
        if (!addr) {
                printk("%s bad page address!\n", __func__);
                return -EINVAL;
        }
        sprintf((char *)addr, "BiscuitOS-%s", __func__);
        printk("[%#lx] %s\n", (unsigned long)addr, (char *)addr);

        /* free all pages to buddy */
        free_pages((unsigned long)addr, order);

        return 0;
}


ZONE_DMA32 中分配物理页

从 ZONE_DMA32 中分配物理内存,开发者可以参考如下代码:

#include <linux/gfp.h>
#include <linux/mm.h>

static int TestCase_alloc_page_from_DMA32(void)
{
        struct page_bs *pages;
        int order = 2;
        void *addr;

        /* allocate page from Buddy (DMA32) */
        pages = alloc_pages(GFP_DMA32, order);
        if (!pages) {
                printk("%s error\n", __func__);
                return -ENOMEM;
        }

        /* Obtain page virtual address */
        addr = page_address(pages);
        if (!addr) {
                printk("%s bad page address!\n", __func__);
                return -EINVAL;
        }
        sprintf((char *)addr, "BiscuitOS-%s", __func__);
        printk("[%#lx] %s\n", (unsigned long)addr, (char *)addr);

        /* free all pages to buddy */
        free_pages((unsigned long)addr, order);

        return 0;
}


ZONE_NORMAL 中分配物理页

从 ZONE_NORMAL 中分配物理内存,开发者可以参考如下代码:

#include <linux/gfp.h>
#include <linux/mm.h>

static int TestCase_alloc_page_from_NORMAL(void)
{
        struct page_bs *pages;
        int order = 2;
        void *addr;

        /* allocate page from Buddy (NORMAL) */
        pages = alloc_pages(GFP_NORMAL, order);
        if (!pages) {
                printk("%s error\n", __func__);
                return -ENOMEM;
        }

        /* Obtain page virtual address */
        addr = page_address(pages);
        if (!addr) {
                printk("%s bad page address!\n", __func__);
                return -EINVAL;
        }
        sprintf((char *)addr, "BiscuitOS-%s", __func__);
        printk("[%#lx] %s\n", (unsigned long)addr, (char *)addr);

        /* free all pages to buddy */
        free_pages((unsigned long)addr, order);

        return 0;
}


ZONE_HIGHMEM 中分配物理页

从 ZONE_HIGHMEM 中分配物理内存,开发者可以参考如下代码:

#include <linux/gfp.h>
#include <linux/mm.h>
#include <linux/highmem.h>

static int TestCase_alloc_page_from_HIGHMEM(void)
{
        struct page_bs *pages;
        int order = 2;

        /* allocate page from Buddy (NORMAL) */
        pages = alloc_pages(__GFP_HIGHMEM, order);
        if (!pages) {
                printk("%s error\n", __func__);
                return -ENOMEM;
        }

	/* Do other mapping! */

        /* free all pages to buddy */
        __free_pages(pages, order);

        return 0;
}


buddy 分配器实践


实践准备

本实践是基于 BiscuitOS Linux 5.0 ARM32 环境进行搭建,因此开发者首先 准备实践环境,请查看如下文档进行搭建:


实践部署

准备好基础开发环境之后,开发者接下来部署项目所需的开发环境。由于项目 支持多个版本的 buddy,开发者可以根据需求进行选择,本文以 linux 2.6.12 版本的 buddy 进行讲解。开发者使用如下命令:

cd BiscuitOS/
make linux-5.0-arm32_defconfig
make menuconfig

选择并进入 “[*] Package —>” 目录。

选择并进入 “[*] Memory Development History —>” 目录。

选择并进入 “[*] Buddy Allocator —>” 目录。

选择 “[*] buddy on linux 2.6.12 —>” 目录,保存并退出。接着执行如下命令:

make

成功之后将出现上图的内容,接下来开发者执行如下命令以便切换到项目的路径:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_buddy-2.6.12
make download

至此源码已经下载完成,开发者可以使用 tree 等工具查看源码:

arch 目录下包含内存初始化早期,与体系结构相关的处理部分。mm 目录下面包含 了与各个内存分配器和内存管理行为相关的代码。init 目录下是整个模块的初始化 入口流程。modules 目录下包含了内存分配器的使用例程和测试代码. fs 目录下 包含了内存管理信息输出到文件系统的相关实现。入口函数是 init/main.c 的 start_kernel()。

如果你是第一次使用这个项目,需要修改 DTS 的内容。如果不是可以跳到下一节。 开发者参考源码目录里面的 “BiscuitOS.dts” 文件,将文件中描述的内容添加 到系统的 DTS 里面,”BiscuitOS.dts” 里的内容用来从系统中预留 100MB 的物理 内存供项目使用,具体如下:

开发者将 “BiscuitOS.dts” 的内容添加到:

BiscuitOS/output/linux-5.0-arm32/linux/linux/arch/arm/boot/dts/vexpress-v2p-ca9.dts

添加完毕之后,使用如下命令更新 DTS:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_buddy-2.6.12
make kernel


实践执行

环境部署完毕之后,开发者可以向通用模块一样对源码进行编译和安装使用,使用 如下命令:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_buddy-2.6.12
make

以上就是模块成功编译,接下来将 ko 模块安装到 BiscuitOS 中,使用如下命令:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_buddy-2.6.12
make install
make pack

以上准备完毕之后,最后就是在 BiscuitOS 运行这个模块了,使用如下命令:

cd BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_buddy-2.6.12
make run

在 BiscuitOS 中插入了模块 “BiscuitOS_buddy-2.6.12.ko”,打印如上信息,那么 BiscuitOS Memory Manager Unit History 项目的内存管理子系统已经可以使用, 接下来开发者可以在 BiscuitOS 中使用如下命令查看内存管理子系统的使用情况:

cat /proc/buddyinfo_bs
cat /proc/vmstat_bs


测试建议

BiscuitOS Memory Manager Unit History 项目提供了大量的测试用例用于测试 不同内存分配器的功能。结合项目提供的 initcall 机制,项目将测试用例分作 两类,第一类类似于内核源码树内编译,也就是同 MMU 子系统一同编译的源码。 第二类类似于模块编译,是在 MMU 模块加载之后独立加载的模块。以上两种方案 皆在最大程度的测试内存管理器的功能。

要在项目中使用以上两种测试代码,开发者可以通过项目提供的 Makefile 进行 配置。以 linux 2.6.12 为例, Makefile 的位置如下:

/xspace/OpenSource/BiscuitOS/BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_buddy-2.6.12/BiscuitOS_buddy-2.6.12/Makefile

Makefile 内提供了两种方案的编译开关,例如需要使用打开 buddy 内存管理器的 源码树内部调试功能,需要保证 Makefile 内下面语句不被注释:

$(MODULE_NAME)-m                += modules/buddy/main.o

如果要关闭 buddy 内存管理器的源码树内部调试功能,可以将其注释:

# $(MODULE_NAME)-m                += modules/buddy/main.o

同理,需要打开 buddy 模块测试功能,可以参照下面的代码:

obj-m                             += $(MODULE_NAME)-buddy.o
$(MODULE_NAME)-buddy-m            := modules/buddy/module.o

如果不需要 buddy 模块测试功能,可以参考下面代码, 将其注释:

# obj-m                             += $(MODULE_NAME)-buddy.o
# $(MODULE_NAME)-buddy-m            := modules/buddy/module.o

在上面的例子中,例如打开了 buddy 的模块调试功能,重新编译模块并在 BiscuitOS 上运行,如下图,可以在 “lib/module/5.0.0/extra/” 目录下看到两个模块:

然后先向 BiscuitOS 中插入 “BiscuitOS_buddy-2.6.12.ko” 模块,然后再插入 “BiscuitOS_buddy-2.6.12-buddy.ko” 模块。如下:

以上便是测试代码的使用办法。开发者如果想在源码中启用或关闭某些宏,可以 修改 Makefile 中内容:

从上图可以知道,如果要启用某些宏,可以在 ccflags-y 中添加 “-D” 选项进行 启用,源码的编译参数也可以添加到 ccflags-y 中去。开发者除了使用上面的办法 进行测试之外,也可以使用项目提供的 initcall 机制进行调试,具体请参考:

Initcall 机制提供了以下函数用于 buddy 调试:

buddy_initcall_bs()

从项目的 Initcall 机制可以知道,buddy_initcall_bs() 调用的函数将 在 buddy 分配器初始化完毕之后自动调用,因此可用此法调试 Buddy 内存分配器。 buddy 相关的测试代码位于:

BiscuitOS/output/linux-5.0-arm32/package/BiscuitOS_buddy-2.6.12/BiscuitOS_buddy-2.6.12/module/buddy/

在 Makefile 中打开调试开关:

$(MODULE_NAME)-m                  += modules/buddy/main.o

模块方式调试 Buddy 分配器,在 Makefile 中打开调试开关:

obj-m                             += $(MODULE_NAME)-buddy.o
$(MODULE_NAME)-buddy-m            := modules/buddy/module.o


buddy 历史补丁


buddy Linux 2.6.12

Linux 2.6.12 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

对于 Linux 2.6.12 的补丁,Linus 将 Linux 内核源码树加入到 git 中来,因此 这个版本的代码均不产生补丁。更多补丁的使用请参考:


buddy Linux 2.6.12.1

Linux 2.6.12.1 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相比 Linux 2.6.12 版本,该版本并未产生 Buddy 分配器相关的补丁。更多补丁 的使用请参考:


buddy Linux 2.6.12.2

Linux 2.6.12.2 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相比 Linux 2.6.12.1 版本,该版本并未产生 Buddy 分配器相关的补丁。更多补丁 的使用请参考:


buddy Linux 2.6.12.3

Linux 2.6.12.3 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相比 Linux 2.6.12.2 版本,该版本并未产生 Buddy 分配器相关的补丁。更多补丁 的使用请参考:


buddy Linux 2.6.12.4

Linux 2.6.12.4 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相比 Linux 2.6.12.3 版本,该版本并未产生 Buddy 分配器相关的补丁。更多补丁 的使用请参考:


buddy Linux 2.6.12.5

Linux 2.6.12.5 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相比 Linux 2.6.12.4 版本,该版本并未产生 Buddy 分配器相关的补丁。更多补丁 的使用请参考:


buddy Linux 2.6.12.6

Linux 2.6.12.6 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相比 Linux 2.6.12.5 版本,该版本并未产生 Buddy 分配器相关的补丁。更多补丁 的使用请参考:


buddy Linux 2.6.13

Linux 2.6.13 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相对上一个版本 linux 2.6.12.6,Buddy 内存分配器增加了多个补丁,如下:

tig mm/page_alloc.c include/linux/gfp.h include/linux/mmzone.h include/linux/mm.h

2005-05-01 08:58 Nick Piggin       o [PATCH] mempool: NOMEMALLOC and NORETRY
                                     [main] b84a35be0285229b0a8a5e2e04d79360c5b75562
2005-06-21 17:14 Nikita Danilov    o [PATCH] mm: add /proc/zoneinfo
                                     [main] 295ab93497ec703f7d6eaf0787dd9768b83035fe
2005-06-21 17:14 Martin Hicks      o [PATCH] VM: early zone reclaim
                                     [main] 753ee728964e5afb80c17659cc6c3a6fd0a42fe0 
2005-06-21 17:14 Martin Hicks      o [PATCH] VM: add __GFP_NORECLAIM
                                     [main] 0c35bbadc59f5ed105c34471143eceb4c0dd9c95 
2005-06-21 17:14 Martin Hicks      o [PATCH] VM: rate limit early reclaim
                                     [main] 1e7e5a9048b30c57ba1ddaa6cdf59b21b65cde99  
2005-06-21 17:14 Darren Hart       o [PATCH] vm: try_to_free_pages unused argument
                                     [main] 1ad539b2bd89bf2e129123eb24d5bcc4484a35de 
2005-06-21 17:14 Benjamin LaHaise  o [PATCH] __mod_page_state(): pass unsigned long instead of unsigned
                                     [main] 83e5d8f7253cb7b14472385a6d57df1e9f848e8e
2005-06-21 17:14 Benjamin LaHaise  o [PATCH] __read_page_state(): pass unsigned long instead of unsigned
                                     [main] c2f29ea111e3344ed48257c2a142c3db514e1529 
2005-06-21 17:14 Janet Morgan      o [PATCH] add OOM debug
                                     [main] 578c2fd6a7f378434655e5c480e23152a3994404 
2005-06-21 17:15 Hugh Dickins      o [PATCH] bad_page: clear reclaim and slab
                                     [main] 334795eca421287c41c257992027d29659dc0f97 
2005-06-21 17:15 Denis Vlasenko    o [PATCH] Kill stray newline
                                     [main] c0d62219a48bd91ec40fb254c930914dccc77ff1 
2005-06-23 00:07 Dave Hansen       o [PATCH] sparsemem base: reorganize page->flags bit operations
                                     [main] 348f8b6c4837a07304d2f72b11ce8d96588065e0 
2005-06-23 00:07 Dave Hansen       o [PATCH] Introduce new Kconfig option for NUMA or DISCONTIG
                                     [main] 93b7504e3e6c1d98586854806e51bea329ea3aa9 
2005-06-27 14:36 Bob Picco         o [PATCH] fix WANT_PAGE_VIRTUAL in memmap_init
                                     [main] 3212c6be251219c0f4c2df0c93e122ff5be0d9dc 
2005-07-07 17:56 Marcelo Tosatti   o [PATCH] print order information when OOM killing
                                     [main] 79b9ce311e192e9a31fd9f3cf1ee4a4edf9e2650 
2005-07-07 17:56 Marcelo Tosatti   o [PATCH] remove completly bogus comment inside __alloc_pages()
                                     [main] 37b173a4d03d1681e6c9529bc43d7a3308132db6 
2005-07-07 17:56 Alexey Dobriyan   o [PATCH] propagate __nocast annotations
                                     [main] 0db925af1db5f3dfe1691c35b39496e2baaff9c9 
2005-07-27 11:44 Andy Whitcroft    o [PATCH] Remove bogus warning in page_alloc.c
                                     [main] 12b1c5f382194d3f656e78fb5c9c8f2bfbe8ed8a 
2005-07-29 22:59 Martin J. Bligh   o [PATCH] Fix NUMA node sizing in nr_free_zone_pages
                                     [main] e310fd43256b3cf4d37f6447b8f7413ca744657a 

git format-patch -1 b84a35be0285229b0a8a5e2e04d79360c5b75562
vi 0001-PATCH-mempool-NOMEMALLOC-and-NORETRY.patch

该补丁向 Buddy 管理器中添加了 “__GFP_NOMEMPOOL” 标志,使用该标志之后 当从 MEMPOOL 中分配物理内存的时候,如果分配表值中包含了 “__GFP_NOMEMPOOL” 标志,那么将从 Buddy 内存分配器中获得物理页而不是从 MEMPOOL 中获得物理内存.

git format-patch -1 295ab93497ec703f7d6eaf0787dd9768b83035fe 
vi 0001-PATCH-mm-add-proc-zoneinfo.patch

该补丁用于向系统添加 “/proc/zoneinfo”,以便从用户空间获得 ZONE 的信息, 在 BiscuitOS 上使用情况如下:

git format-patch -1 753ee728964e5afb80c17659cc6c3a6fd0a42fe0 
vi 0001-PATCH-VM-early-zone-reclaim.patch

该补丁是向 Buddy 内存分配器添加了页回收机制,并在 IA64 和 i386 架构上添加 了新的系统调用 sys_set_zone_reclaim. 首先在 struct zone 结构中添加了 “reclaim_pages” 成员,并在 “__alloc_pages()” 中添加了 zone_reclaim_retry 处理,当当前 ZONE 可用物理内存小于 watermark 之后,如果 should_reclaim_zone() 确认可以回收物理页,那么 Buddy 内存分配器就执行 zone_reclaim() 进行物理页 回收,回收完成之后,Buddy 内存分配器再次分配物理内存。sys_set_zone_reclaim 系统调用主要是用于设置 Buddy 内存分配器是否支持页回收机制。

git format-patch -1 0c35bbadc59f5ed105c34471143eceb4c0dd9c95 
vi 0001-PATCH-VM-add-__GFP_NORECLAIM.patch

该补丁向 Buddy 内存管理器中添加了 __GFP_NORECLAIM 标志,以便在 Buddy 分配 器在没有可用物理内存的时候,不执行回收策略。

git format-patch -1 1e7e5a9048b30c57ba1ddaa6cdf59b21b65cde99
vi 0001-PATCH-VM-rate-limit-early-reclaim.patch

该补丁用于在 struct zone 中添加 “reclaim_in_progress” 成员,该成员用于 统计标记该 ZONE 是否正在进行页回收操作. 每当 ZONE 进行页回收的时候就会 增加 reclaim_in_progress 的值,以告诉其他对 ZONE 进行页回收操作当前 ZONE 正在进行页回收操作。页回收结束之后再减少 reclaim_in_progress 的引用计数。

git format-patch -1 1ad539b2bd89bf2e129123eb24d5bcc4484a35de 
vi 0001-PATCH-vm-try_to_free_pages-unused-argument.patch

该补丁用于修改的 try_to_free_pages() 函数的参数,移除了 order 参数. 移除 的原因是从 2.6.0 开始,order 参数就没有使用过.

git format-patch -1 83e5d8f7253cb7b14472385a6d57df1e9f848e8e
vi 0001-PATCH-__mod_page_state-pass-unsigned-long-instead-of.patch

该补丁用于将 __mod_page_state() 函数的 offset 参数类型由 unsigned 转变为 unsigned long。

git format-patch -1 c2f29ea111e3344ed48257c2a142c3db514e1529 
vi 0001-PATCH-__read_page_state-pass-unsigned-long-instead-o.patch

该补丁用于修改 __read_page_state() 函数 offset 参数的类型,由 unsigned 转变为 unsigned long. 以便解决一些编译遇到的问题.

git format-patch -1 578c2fd6a7f378434655e5c480e23152a3994404 
vi 0001-PATCH-add-OOM-debug.patch

该补丁用于在出现 OOM 时候打印所有 mem 信息。并且在 Buddy 分配器无法分配 可用物理内存的时候,打印当前内存信息.

git format-patch -1 334795eca421287c41c257992027d29659dc0f97 
vi 0001-PATCH-bad_page-clear-reclaim-and-slab.patch

该补丁在 bad_page() 函数判断 page 标志是增加了 PG_slab、PG_private 和 PG_reclaim 标志. 在 prep_new_page() 函数中添加了对 page 映射数量、是否 映射和引用计数进行检测,并对 page 的 flags 检测时增加了 PG_slab 检测。 以上的修改在 Buddy 分配器添加了 PG_slab 和 PG_reclaim 标志的支持,以便 free_pages_check() 的正确检测.

git format-patch -1 c0d62219a48bd91ec40fb254c930914dccc77ff1
vi 0001-PATCH-Kill-stray-newline.patch

该补丁修改了打印信息的格式.

git format-patch -1 348f8b6c4837a07304d2f72b11ce8d96588065e0 
vi 0001-PATCH-sparsemem-base-reorganize-page-flags-bit-opera.patch

该补丁基于 NODE 相关信息重新规划了 ZONES_MASK, ZONETABLE_MASK 和 NODES_MASK 等宏,以此重新规划了 zone_table[] 的布局,这样的修改影响到了 page_zonenum(), page_to_nid(), page_zone() 等函数的实现。并在初始化每个 page 的时候,使用 set_page_links() 将 page 关联到相应的 NODE 和 ZONE 上。

git format-patch -1 93b7504e3e6c1d98586854806e51bea329ea3aa9 
vi 0001-PATCH-Introduce-new-Kconfig-option-for-NUMA-or-DISCO.patch

该补丁新增了一个宏 CONFIG_NEED_MULTIPLE_NODES, 并在 Buddy 分配器相关的代码里, 将 CONFIG_DISCONTIGMEM 替换成 CONFIG_NEED_MULTIPLE_NODES,在函数 “free_area_init()” 中将原先的 contig_page_data 替换成了 NODE_DATA(0)。

git format-patch -1 3212c6be251219c0f4c2df0c93e122ff5be0d9dc 
vi 0001-PATCH-fix-WANT_PAGE_VIRTUAL-in-memmap_init.patch

该补丁用于在 Buddy 分配器初始化每个 page 的时候,如果系统支持 WANT_PAGE_VIRTUAL, 那么如果这个 page 不是 ZONE_HIGHMEM,那么 memap_init_zone() 函数就会给 这个 page 设置一个线性地址,因为非 ZONE_HIGHMEM 的 page 对应的物理地址和 虚拟地址都是线性映射的.

git format-patch -1 79b9ce311e192e9a31fd9f3cf1ee4a4edf9e2650 
vi 0001-PATCH-print-order-information-when-OOM-killing.patch

该补丁修改了 out_of_memory() 的参数,添加了 order 参数用于指定 Buddy 分配器中是哪条 free_area[] 上没有可用物理内存了。

git format-patch -1 37b173a4d03d1681e6c9529bc43d7a3308132db6 
vi 0001-PATCH-remove-completly-bogus-comment-inside-__alloc_.patch 

该补丁从 Buddy 内存分配器的 __alloc_oages() 函数中移除了一些注释.

git format-patch -1 0db925af1db5f3dfe1691c35b39496e2baaff9c9 
vi 0001-PATCH-propagate-__nocast-annotations.patch 

该补丁用于将与 Buddy 内存分配器相关的函数中,gfp 参数的类型修改为 “unsinged int __nocast” 类型,以便内核做静态检测. 并对 __GFP_DMA 和 __GFP_HIGHMEM 宏的值增加了 “u” 限制.

git format-patch -1 12b1c5f382194d3f656e78fb5c9c8f2bfbe8ed8a 
vi 0001-PATCH-Remove-bogus-warning-in-page_alloc.c.patch

该补丁在 Buddy 内存分配器创建过程中,移除了 ZONE 对齐的一些操作.

git format-patch -1 e310fd43256b3cf4d37f6447b8f7413ca744657a
vi 0001-PATCH-Fix-NUMA-node-sizing-in-nr_free_zone_pages.patch 

该补丁用于在计算一个 ZONE 上物理的数量支持了多 NODE. 更多补丁的使用请参考:


buddy Linux 2.6.13.1

Linux 2.6.13.1 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相比 Linux 2.6.13 版本,该版本并未产生 Buddy 分配器相关的补丁。更多补丁 的使用请参考:


buddy Linux 2.6.14

Linux 2.6.14 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相对上一个版本 linux 2.6.13.1,Buddy 内存分配器增加了多个补丁,如下:

tig mm/page_alloc.c include/linux/gfp.h include/linux/mmzone.h include/linux/mm.h

2005-09-03 15:54 Nick Piggin              o [PATCH] mm: remove atomic
                                            [main] 242e54686257493f0b10ac557e730419d9af7d24
2005-09-03 15:54 Martin Hicks             o [PATCH] VM: zone reclaim atomic ops cleanup
                                            [main] 53e9a6159fdc6419874ce4d86d3577dbedc77b62
2005-09-03 15:55 Martin Hicks             o [PATCH] VM: add page_state info to per-node meminfo
                                            [main] c07e02db76940c75fc92f2f2c9adcdbb09ed70d0
2005-09-06 15:16 Christoph Lameter        o [PATCH] More __read_mostly variables
                                            [main] c3d8c1414573be8cf7c8fdc1e076935697c7f6af
2005-09-06 15:17 Ravikiran G Thirumalai   o [PATCH] Additions to .data.read_mostly section
                                            [main] 6c231b7bab0aa6860cd9da2de8a064eddc34c146
2005-09-06 15:18 Paul Jackson             o [PATCH] cpusets: new __GFP_HARDWALL flag
                                            [main] f90b1d2f1aaaa40c6519a32e69615edc25bb97d5
2005-09-06 15:18 Paul Jackson             o [PATCH] cpusets: formalize intermediate GFP_KERNEL containment
                                            [main] 9bf2229f8817677127a60c177aefce1badd22d7b
2005-09-10 00:26 Renaud Lienhart          o [PATCH] remove invalid comment in mm/page_alloc.c
                                            [main] 207f36eec9e7b1077d7a0aaadb4800e2c9b4cfa4
2005-09-13 01:25 Randy Dunlap             o [PATCH] use add_taint() for setting tainted bit flags
                                            [main] 9f1583339a6f52c0c26441d39a0deff8246800f7
2005-10-07 07:46 Al Viro                  o [PATCH] gfp flags annotations - part 1
                                            [main] dd0fc66fb33cd610bc1a5db8a5e232d34879b4d7

git format-patch -1 242e54686257493f0b10ac557e730419d9af7d24 
vi 0001-PATCH-mm-remove-atomic.patch

该补丁添加了 “__ClearPageDirty(page)”, 用于标记某个页变沾了,并在 Buddy 内存分配器分配过程中,对可用的物理进行检测的时候,如果检测到页是沾的,那么 调用该函数.

git format-patch -1 53e9a6159fdc6419874ce4d86d3577dbedc77b62 
vi 0001-PATCH-VM-zone-reclaim-atomic-ops-cleanup.patch

该补丁由于修改 struct zone 的 reclaim_in_progress 成员,该成员用于标记 当前 ZONE 是否在进行页回收。在 Buddy 内存管理器初始化的时候,将各个 ZONE 的 reclaim_in_progress 初始化为 0,并在 shrik_zone() 外面的 reclaim_in_progress 操作全部移到了 shrink_zone() 函数内部.

git format-patch -1 c07e02db76940c75fc92f2f2c9adcdbb09ed70d0
vi 0001-PATCH-VM-add-page_state-info-to-per-node-meminfo.patch

该补丁修改 Buddy 内存管理器中 page 使用状况相关的函数. 添加了 cpu_mask 的 支持. 补丁还添加了 NODE 节点内存信息的打印.

git format-patch -1 c3d8c1414573be8cf7c8fdc1e076935697c7f6af
vi 0001-PATCH-More-__read_mostly-variables.patch

该补丁用于对 Buddy 内存管理器使用的一些数据定义为 “__read_mostly”.

git format-patch -1 6c231b7bab0aa6860cd9da2de8a064eddc34c146
vi 0001-PATCH-Additions-to-.data.read_mostly-section.patch

该补丁用于对 Buddy 内存管理器使用的一些数据定义为 “__read_mostly”.

git format-patch -1 f90b1d2f1aaaa40c6519a32e69615edc25bb97d5
vi 0001-PATCH-cpusets-new-__GFP_HARDWALL-flag.patch

向 Buddy 内存分配器添加了 __GFP_HARDWALL 标志.

git format-patch -1 9bf2229f8817677127a60c177aefce1badd22d7b 
vi 0001-PATCH-cpusets-formalize-intermediate-GFP_KERNEL-cont.patch

该补丁修改了 cpuset_zone_allowed() 函数,添加了 gfp_mask 参数,补丁将 Buddy 内存分配器调用的 cpuset_zone_allowed() 函数的 gfp_mask 参数全部设置为 __GFP_HARDWALL。

git format-patch -1 207f36eec9e7b1077d7a0aaadb4800e2c9b4cfa4 
vi 0001-PATCH-remove-invalid-comment-in-mm-page_alloc.c.patch

该补丁修改了注释,表明 free_pages_bulk() 不支持 count 为 0 的 list 释放.

git format-patch -1 9f1583339a6f52c0c26441d39a0deff8246800f7
vi 0001-PATCH-use-add_taint-for-setting-tainted-bit-flags.patch

该补丁向 Buddy 内存分配器的 bad_page() 函数添加了 add_taile() 的处理,移除 之前的同类代码.

git format-patch -1 dd0fc66fb33cd610bc1a5db8a5e232d34879b4d7 
vi 0001-PATCH-gfp-flags-annotations-part-1.patch

该补丁用于将 Buddy 内存分配器相关函数的 gfp 参数全部修改为 gfp_t 类型. 更多补丁的使用请参考:


buddy Linux 2.6.15

Linux 2.6.15 依旧采用 buddy 作为其物理内存管理器。

Buddy 内存分配
alloc_pages_node
alloc_pages_current
__get_free_pages
get_zeroed_page
__alloc_pages
__get_free_pages
Buddy 内存释放
__alloc_pages
__free_pages
free_pages
__free_page
free_page
Buddy 其他操作
gfp_zone
page_alloc_init
nr_free_pages
nr_free_highpages

具体函数解析说明,请查看:

与项目相关

buddy 内存分配器与本项目相关的调用顺序如下:

补丁

相对上一个版本 linux 2.6.13.1,Buddy 内存分配器增加了多个补丁,如下:

tig mm/page_alloc.c include/linux/gfp.h include/linux/mmzone.h include/linux/mm.h

2005-10-21 02:55 Al Viro          o [PATCH] gfp_t: infrastructure
                                    [main] af4ca457eaf2d6682059c18463eb106e2ce58198
2005-10-21 03:22 Al Viro          o [PATCH] gfp_t: the rest
                                    [main] 260b23674fdb570f3235ce55892246bef1c24c2a
2005-10-29 18:16 Nick Piggin      o [PATCH] core remove PageReserved
                                    [main] b5810039a54e5babf428e9a1e89fc1940fabff11
2005-10-29 18:16 Hugh Dickins     o [PATCH] mm: split page table lock
                                    [main] 4c21e2f2441dc5fbb957b030333f5a3f2d02dea7
2005-10-29 18:16 Dave Hansen      o [PATCH] memory hotplug prep: break out zone initialization
                                    [main] ed8ece2ec8d3c2031b1a1a0737568bb0d49454e0
2005-10-29 18:16 Dave Hansen      o [PATCH] memory hotplug locking: node_size_lock
                                    [main] 208d54e5513c0c02d85af0990901354c74364d5c
2005-10-29 18:16 Dave Hansen      o [PATCH] memory hotplug locking: zone span seqlock
                                    [main] bdc8cb984576ab5b550c8b24c6fa111a873503e3
2005-10-29 18:17 John Hawkes      o [PATCH] mm: wider use of for_each_*cpu()
                                    [main] 2f96996de0eda378df2a5f857ee1ef615ae10a4f
2005-11-07 01:01 Adrian Bunk      o [PATCH] unexport nr_swap_pages
                                    [main] f8b8db77b0cc36670ef4ed6bc31e64537ffa197e
2005-11-10 15:45 Dave Jones       o [PATCH] Don't print per-cpu vm stats for offline cpus.
                                    [main] 6b482c6779daaa893b277fc9b70767a7c2e7c5eb
2005-11-13 16:06 Kirill Korotaev  o [PATCH] mm: __GFP_NOFAIL fix
                                    [main] 885036d32f5d3c427c3e2b385b5a5503805e3e52
2005-11-13 16:06 Rohit Seth       o [PATCH] mm: __alloc_pages cleanup
                                    [main] 7fb1d9fca5c6e3b06773b69165a73f3fb786b8ee
2005-11-13 16:06 Paul Jackson     o [PATCH] mm: gfp_noreclaim cleanup
                                    [main] 2d6c666e8704cf06267f29a4fa3d2cf823469c38
2005-11-13 16:06 Nick Piggin      o [PATCH] mm: highmem watermarks
                                    [main] 669ed17521b9b78cdbeac8a53c30599aca9527ce
2005-11-05 17:25 Andi Kleen       o [PATCH] x86_64: Add 4GB DMA32 zone
                                    [main] a2f1b424900715ed9d1699c3bb88a434a2b42bc0
2005-11-05 17:25 Andi Kleen       o [PATCH] x86_64: Remove obsolete ARCH_HAS_ATOMIC_UNSIGNED and page_flags_t
                                    [main] 07808b74e7dab1aa385e698795875337d72daf7d
2005-11-17 21:35 Jens Axboe       o [PATCH] VM: fix zone list restart in page allocatate
                                    [main] 6b1de9161e973bac8c4675db608fe4f38d2689bd
2005-11-21 21:32 Hugh Dickins     o [PATCH] unpaged: unifdefed PageCompound
                                    [main] 664beed0190fae687ac51295694004902ddeb18e
2005-11-21 21:32 Hugh Dickins     o [PATCH] unpaged: PG_reserved bad_page
                                    [main] 689bcebfda16d7bace742740bfb3137fff30b529
2005-11-22 19:39 Linus Torvalds   o Fix up GFP_ZONEMASK for GFP_DMA32 usage
                                    [main] ac3461ad632e86e7debd871776683c05ef3ba4c6
2005-11-28 13:44 Nick Piggin      o [PATCH] mm: __alloc_pages cleanup fix
                                    [main] 3148890bfa4f36c9949871264e06ef4d449eeff9

git format-patch -1 af4ca457eaf2d6682059c18463eb106e2ce58198 
vi 0001-PATCH-gfp_t-infrastructure.patch 

该补丁为 Buddy 内存分配器的标志添加了 __force 进行 sparse 静态强制类型 检测。

git format-patch -1 260b23674fdb570f3235ce55892246bef1c24c2a 
vi 0001-PATCH-gfp_t-the-rest.patch 

该补丁继续将 Buddy 内存管理器核心函数内的 gfp 标志的类型设置为 gfp_t. 添加 了 highest_zone() 实现,这在建立每个 ZONE 的后备 ZONE 的时候提供了便捷 接口.

git format-patch -1 b5810039a54e5babf428e9a1e89fc1940fabff11 
vi 0001-PATCH-core-remove-PageReserved.patch 

该补丁用于 Buddy 分配器在 put_page() 的时候,由原先检测 page 是否预留并且 没有其他地方引用它,修改成了只检测是否有其他地方引用它. Buddy 分配器在检测 某个页是否属于 Buddy 页的标志中移除了预留页的检测,在将 PG_reserved 和 PG_writeback 两个标志加入到 free_page_check 过程中.

git format-patch -1 4c21e2f2441dc5fbb957b030333f5a3f2d02dea7 
vi 0001-PATCH-mm-split-page-table-lock.patch 

该补丁将 struct page 的 private 成员与新的 ptl 成员组成新的联合体 u. 并且 提供了 private 成员的访问接口 “page_private()” 和 “set_page_private()”.

git format-patch -1 ed8ece2ec8d3c2031b1a1a0737568bb0d49454e0 
vi 0001-PATCH-memory-hotplug-prep-break-out-zone-initializat.patch

该补丁用于当在 Buddy 内存分配器初始化过程中,如果 ZONE 是一个空的,那么 也要兼容空 ZONE 的初始化。

git format-patch -1 208d54e5513c0c02d85af0990901354c74364d5c
vi 0001-PATCH-memory-hotplug-locking-node_size_lock.patch 

该补丁在 struct pglist_data 结构中添加了与 CONFIG_MEMORY_HOTPLUG 相关的 新成员 node_size_lock 锁。Buddy 内存分配器初始化过程中,会调用 pgdat_resize_init() 函数.

git format-patch -1 bdc8cb984576ab5b550c8b24c6fa111a873503e3 
vi 0001-PATCH-memory-hotplug-locking-zone-span-seqlock.patch

该补丁向 struct zone 中增加了 span_seqlock 成员,该成员用于操作当前 ZONE 的 spanned_pages 成员时使用读写锁. 并在 Buddy 分配器初始化的时候添加对该 读写锁的初始化。

git format-patch -1 2f96996de0eda378df2a5f857ee1ef615ae10a4f 
vi 0001-PATCH-mm-wider-use-of-for_each_-cpu.patch 

该补丁用于在 Buddy 管理器内部,show_free_areas() 在打印所有 ZONE 内存信息 的时候,将遍历所有 CPU 的函数提出按成了 for_each_cpu(), 该函数会遍历系统 所有可以使用的 CPU,因此不需要再次调用 cpu_possible() 进行确认了.

git format-patch -1 f8b8db77b0cc36670ef4ed6bc31e64537ffa197e 
vi 0001-PATCH-unexport-nr_swap_pages.patch 

该补丁取消将 nr_swap_pages 导出到全局的符号表里.

git format-patch -1 6b482c6779daaa893b277fc9b70767a7c2e7c5eb
vi 0001-PATCH-Don-t-print-per-cpu-vm-stats-for-offline-cpus.patch 

该补丁用于 Buddy 内存分配器的 show_free_area() 打印所有 ZONE 内存信息的时候, 遍历所有的 CPU,从原先的 for_each_cpu() 替换成了 for_each_online_cpu(), 这样将遍历当前正在使用的 CPU.

git format-patch -1 885036d32f5d3c427c3e2b385b5a5503805e3e52 
vi 0001-PATCH-mm-__GFP_NOFAIL-fix.patch 

该补丁用于在 Buddy 内存分配器分配物理内存的时候,当没有可用物理内存分配的 时候,如果此时分配标志中包含了 __GFP_NOFAIL, 那么函数就调用 blk_congestion_write() 获得更多可用物理页,然后跳转到 nofail_alloc 处重新 分配物理内存.

git format-patch -1 7fb1d9fca5c6e3b06773b69165a73f3fb786b8ee 
vi 0001-PATCH-mm-__alloc_pages-cleanup.patch

该补丁重新整理了 Buddy 内存分配器 __alloc_pages() 的过程,新建立了函数 get_page_from_freelist(), 并将函数替代的代码移除。并增加了 ALLOC_ 标志控制 整个分配过程.

git format-patch -1 2d6c666e8704cf06267f29a4fa3d2cf823469c38
vi 0001-PATCH-mm-gfp_noreclaim-cleanup.patch

该补丁用于从 Buddy 内存管理器中移除 __GFP_NORECLAIM 标志.

git format-patch -1 669ed17521b9b78cdbeac8a53c30599aca9527ce 
vi 0001-PATCH-mm-highmem-watermarks.patch 

该补丁为 ZONE_HIGHMEM 也添加了水位线.

git format-patch -1 a2f1b424900715ed9d1699c3bb88a434a2b42bc0
vi 0001-PATCH-x86_64-Add-4GB-DMA32-zone.patch

该补丁用于向 X86_64 系统 4GB 内存的情况下增加 DMA32 ZONE, 并未 ZONE_DMA32 设置后备 ZONE 分区。

git format-patch -1 07808b74e7dab1aa385e698795875337d72daf7d
vi 0001-PATCH-x86_64-Remove-obsolete-ARCH_HAS_ATOMIC_UNSIGNE.patch 

该补丁将 struct page 的 flags 成员的类型修改为 unsigned long 型.

git format-patch -1 6b1de9161e973bac8c4675db608fe4f38d2689bd 
vi 0001-PATCH-VM-fix-zone-list-restart-in-page-allocatate.patch 

该补丁微调正了 Buddy 分配器分配内存的细节.

git format-patch -1 664beed0190fae687ac51295694004902ddeb18e
vi 0001-PATCH-unpaged-unifdefed-PageCompound.patch

该补丁移除了 CONFIG_HUGETLB_PAGE 的一些设置.

git format-patch -1 689bcebfda16d7bace742740bfb3137fff30b529
vi 0001-PATCH-unpaged-PG_reserved-bad_page.patch

该补丁让预留页测试不能被 free 之后供 Buddy 分配器分配.

git format-patch -1 ac3461ad632e86e7debd871776683c05ef3ba4c6
vi 0001-Fix-up-GFP_ZONEMASK-for-GFP_DMA32-usage.patch

该补丁修改 GFP_ 标志与具体 ZONE 的匹配逻辑.

git format-patch -1 3148890bfa4f36c9949871264e06ef4d449eeff9 
vi 0001-PATCH-mm-__alloc_pages-cleanup-fix.patch 

该补丁向 Buddy 内存管理器分配物理页时,新增了多个 ALLOC_ 与水位线有关的标志. 更多补丁的使用请参考:


buddy 历史时间轴


buddy API

alloc_large_system_hash
void *__init alloc_large_system_hash(const char *tablename,
                                     unsigned long bucketsize,
                                     unsigned long numentries,
                                     int scale,
                                     int flags,
                                     unsigned int *_hash_shift,
                                     unsigned int *_hash_mask,
                                     unsigned long limit)
  作用: 为特定子系统分配一个指定大小的 Hash 表.
alloc_node_mem_map
static void __init alloc_node_mem_map(struct pglist_data *pgdat)
  作用: 为 Buddy 内存管理器分配 mem_map[] 数组,管理所有可用物理页.
__alloc_pages
struct page * fastcall
__alloc_pages(gfp_t gfp_mask, unsigned int order,
                struct zonelist *zonelist)
  作用: Buddy 分配器分配物理页核心实现.
alloc_pages
#define alloc_pages(gfp_mask, order) \
                alloc_pages_node(numa_node_id(), gfp_mask, order)
  作用: 从 Buddy 管理器分配指定数量的物理页.
alloc_pages_node
static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
                                                unsigned int order)
  作用: Buddy 分配器从指定的 NODE 中分配指定数量的物理页.
bad_page
static void bad_page(const char *function, struct page *page)
  作用: 检查 page 是否是坏的.
bad_range
static int bad_range(struct zone *zone, struct page *page)
  作用: 检查页的范围是否合法.
buffered_rmqueue
static struct page *
buffered_rmqueue(struct zone *zone, int order, gfp_t gfp_flags)
  作用: 从 Buddy 内存管理器中请求指定数量的物理页.
build_all_zonelists
void __init build_all_zonelists(void)
  作用: Buddy 管理器为每个 ZONE 设置后备 ZONE 信息.
build_zonelists
static void __init build_zonelists(pg_data_t *pgdat)
  作用: Buddy 管理器为每个 ZONE 设置后备 ZONE 信息.
calculate_zone_totalpages
static void __init calculate_zone_totalpages(struct pglist_data *pgdat,
                unsigned long *zones_size, unsigned long *zholes_size)
  作用: 统计每个 NODE 上物理页的信息,即可用物理页、真实可用物理页、物理页
        的数量.
expand
static inline struct page *
expand(struct zone *zone, struct page *page,
        int low, int high, struct free_area *area)
  作用: Buddy 管理器从其他 free_area[] 获得更大块的物理页块,将其从中间拆分
        成两块一样的物理页块,然后将其中一块插入到低一级的 free_area[] 链表
        中,如果此时另外一块物理页块还是大于需求的大小,那么 Buddy 继续循环
        上面的操作,知道拆分到合适大小的物理页块, 并返回该物理页.
__find_combined_index
static inline unsigned long
__find_combined_index(unsigned long page_idx, unsigned int order)
  作用: Buddy 管理器获得 "Buddy" 物理页的索引号.
for_each_pgdat
#define for_each_pgdat(pgdat) \
        for (pgdat = pgdat_list; pgdat; pgdat = pgdat->pgdat_next)
  作用: 遍历所有的节点.
for_each_zone
#define for_each_zone(zone) \
        for (zone = pgdat_list->node_zones; zone; zone = next_zone(zone))
  作用: 遍历所有的 ZONE 分区.
free_area_init
void __init free_area_init(unsigned long *zones_size)
  作用: 初始化 Buddy 管理器.
free_area_init_core
static void __init free_area_init_core(struct pglist_data *pgdat,
                unsigned long *zones_size, unsigned long *zholes_size)
  作用: Buddy 管理器初始化核心程序.
free_area_init_node
void __init free_area_init_node(int nid, struct pglist_data *pgdat,
                unsigned long *zones_size, unsigned long node_start_pfn,
                unsigned long *zholes_size)
  作用: Buddy 管理器初始化指定节点上的所有 ZONE 分区.
__free_pages
fastcall void __free_pages(struct page *page, unsigned int order)
  作用: Buddy 管理器释放指定数量的物理页.
free_pages
fastcall void free_pages(unsigned long addr, unsigned int order)
  作用: Buddy 管理器释放指定数量且已经映射的物理页.
__free_pages_bulk
static inline void __free_pages_bulk (struct page *page,
                struct zone *zone, unsigned int order)
  作用: 释放一定数量的物理页块到 Buddy 内存管理器.
free_pages_bulk
static int
free_pages_bulk(struct zone *zone, int count,
                struct list_head *list, unsigned int order)
  作用: 释放一定量的物理页块到 Buddy 管理器里.
free_pages_check
static inline int free_pages_check(const char *function, struct page *page)
  作用: Buddy 管理器对即将释放的页进行检测, 检测是否满足释放的条件.
__free_pages_ok
void __free_pages_ok(struct page *page, unsigned int order)
  作用: 释放指定页到 Buddy 管理器里.
__get_dma_pages
#define __get_dma_pages(gfp_mask, order) \
                __get_free_pages((gfp_mask) | GFP_DMA,(order))
  作用: 从 Buddy 管理器分配指定数量的 DMA 页,并返回其对应的虚拟地址.
__get_free_pages
fastcall unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order)
  作用: 从 Buddy 分配器中分配指定数量的物理页,并返回物理页对应的虚拟地址.
get_page_from_freelist
static struct page *
get_page_from_freelist(gfp_t gfp_mask, unsigned int order,
                struct zonelist *zonelist, int alloc_flags)
  作用: Buddy 管理器从指定 ZONE 的 free_area[] 链表中获得指定数量的物理页.
get_zeroed_page
fastcall unsigned long get_zeroed_page(gfp_t gfp_mask)
  作用: 从 Buddy 分配器中分配一个空白的页,并且获得物理页对应的虚拟地址.
highest_zone
static inline int highest_zone(int zone_bits)
  作用: 设置 ZONE 的最大备用 ZONE.
init_currently_empty_zone
static __devinit void init_currently_empty_zone(struct zone *zone,
                unsigned long zone_start_pfn, unsigned long size)
  作用: Buddy 管理器初始化一个空的 ZONE 分区.
init_per_zone_pages_min
static int __init init_per_zone_pages_min(void)
  作用: 设置 ZONE 的水位线.
is_highmem
static inline int is_highmem(struct zone *zone)
  作用: 判断 ZONE 是否是 ZONE_HIGHMEM
is_highmem_idx
static inline int is_highmem_idx(int idx)
  作用: 判断 ZONE 索引对应的 ZONE 是否是 ZONE_HIGHMEM.
is_normal
static inline int is_normal(struct zone *zone)
  作用: 判断 ZONE 是否是 ZONE_NORMAL。
is_normal_idx
static inline int is_normal_idx(int idx)
  作用: 判断 ZONE 所以对应的 ZONE 是否是 ZONE_NORMAL.
memmap_init_zone
void __devinit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
                unsigned long start_pfn)
  作用: Buddy 内存管理器初始化 ZONE 分区上的每个 page.
__mod_page_state
void __mod_page_state(unsigned long offset, unsigned long delta)
  作用: 读取 Buddy 管理器中指定页的使用情况.
next_zone
static inline struct zone *next_zone(struct zone *zone)
  作用: 从 ZONELIST 中获得下一个可用的 ZONE.
nr_free_buffer_pages
unsigned int nr_free_buffer_pages(void)
  作用: 读取 Buddy 管理器中 Buffer 中物理页的数量.
nr_free_highpages
unsigned int nr_free_highpages (void)
  作用: 读取 Buddy 管理器中 ZONE_HIGHMEM 中物理页的数量.
nr_free_pagecache_pages
unsigned int nr_free_pagecache_pages(void)
  作用: 读取 Buddy 管理器中缓存的物理页数量.
nr_free_pages
unsigned int nr_free_pages(void)
  作用: 读取 Buddy 管理器中所有 ZONE 中空闲物理页的数量.
nr_free_zone_pages
static unsigned int nr_free_zone_pages(int offset)
  作用: 读取 Buddy 管理器中所有 ZONE 的物理页数量.
__page_find_buddy
static inline struct page *
__page_find_buddy(struct page *page, unsigned long page_idx, unsigned int order)
  作用: Buddy 管理器查找 page 对应的 "Buddy" page.
page_is_buddy
static inline int page_is_buddy(struct page *page, int order)
  作用: Buddy 管理器判断物理页块是否属于 Buddy 管理器.
prep_new_page
static int prep_new_page(struct page *page, int order)
  作用: Buddy 管理器从 free_area[] 中获得可用物理页块之后,将物理页块进行
        一些初始化.
page_order
static inline unsigned long page_order(struct page *page)
  作用: 读取 page 的 private 值.
pfn_valid
static inline int pfn_valid(unsigned long pfn)
  作用: 判断一个 PFN 是否有效.
prep_zero_page
static inline void prep_zero_page(struct page *page, int order, gfp_t gfp_flags)
  作用: Buddy 分配器将指定的数量的页内容全部清零.
gfp_zone
static inline int gfp_zone(gfp_t gfp)
  作用: 通过 GFP 标志获得对应的 ZONE 分区.
__read_page_state
unsigned long __read_page_state(unsigned long offset)
  作用: 读取 Buddy 管理器中指定页的使用情况.
__rmqueue
static struct page *__rmqueue(struct zone *zone, unsigned int order)
  作用: Buddy 管理器从 free_area[] 链表中获得大块物理页块之后,将拆分之后的
        物理页块插入指定的 free_area[] 链表,剩余的物理页块返回给调用者.
rmqueue_bulk
static int rmqueue_bulk(struct zone *zone, unsigned int order,
                        unsigned long count, struct list_head *list)
  作用: 从 Buddy 管理器中分配物理页块.
rmv_page_order
static inline void rmv_page_order(struct page *page)
  作用: 清除 page 的 PG_private 标志,并将 page 的 private 设置为 0.
set_hashdist
static int __init set_hashdist(char *str)
  作用: 从 cmdline 中解析 "hashdist" 参数,该参数用于设置 VFS 相关的 Hash 表.
set_page_order
static inline void set_page_order(struct page *page, int order)
  作用: 设置 page 的 private.
set_page_refs
void set_page_refs(struct page *page, int order)
  作用: 设置 Page 的引用计数.
setup_per_zone_lowmem_reserve
static void setup_per_zone_lowmem_reserve(void)
  作用: 设置系统中所有 ZONE 的水位线.
setup_per_zone_pages_min
void setup_per_zone_pages_min(void)
  作用: 设置所有 ZONE 的 pages_low、pages_min 和 pages_high.
show_free_areas
void show_free_areas(void)
  作用: Buddy 管理器用于打印 ZONE 上所有使用情况.
zoneinfo_show
static int zoneinfo_show(struct seq_file *m, void *arg)
  作用: 通过 /proc/zoneinfo 打印所有 ZONE 的信息.
zone_idx
#define zone_idx(zone)          ((zone) - (zone)->zone_pgdat->node_zones)
  作用: 获得 ZONE 对应的 ZONE ID.
zone_init_free_lists
void zone_init_free_lists(struct pglist_data *pgdat, struct zone *zone,
                                unsigned long size)
  作用: Buddy 管理器初始化每个 ZONE 的 free_area[] 数组.
zone_wait_table_init
static __devinit
void zone_wait_table_init(struct zone *zone, unsigned long zone_size_pages)
  作用: Buddy 管理器初始化每个 ZONE 的 wait_table.
zone_watermark_ok
int zone_watermark_ok(struct zone *z, int order, unsigned long mark,
                      int classzone_idx, int alloc_flags)
  作用: Buddy 管理器检查当前 ZONE 的可用物理页是否已经低于 ZONE 的水位线.


buddy 进阶研究


buddy 分配器调试


/proc/zoneinfo

Linux 内核从 linux 2.6.13 开始向 Proc 文件系统添加了 zoneinfo 节点,用于 获得 ZONE 的所有使用信息,开发者参考下列命令使用:

cat /proc/zoneinfo


/proc/buddyinfo

Linux 内核向 Proc 文件系统中添加了 buddyinfo 节点,用于获得每个 NODE 的 free_area[] 各个链表的使用情况, 开发者参考下列命令使用:

cat /proc/buddyinfo


附录

BiscuitOS Home

BiscuitOS Driver

Linux Kernel

Bootlin: Elixir Cross Referencer

捐赠一下吧 🙂

MMU