深入浅出内存管理--alloc_pages与free_page分析(框图)

本文深入探讨了Linux内核中的alloc_pages和free_page函数,详细分析了连续物理内存分配及释放的过程。alloc_pages通过调用buffered_rmqueue实现高效内存分配,利用percpu高速缓存提升性能;free_page则负责释放内存,逆过程执行,确保内存资源的有效回收。

alloc_pages

从前面文章的介绍来看,alloc_pages用于连续物理内存的分配,它的实现如下图所示:
从页分配器到伙伴系统
从这个流程图来分析,函数是一步一步调用到buffered_rmqueue的,alloc_pages是页分配器的对外接口,系统中很多模块和驱动可以直接调用它申请到内存,而buffered_rmqueue是底层伙伴系统算法的实现。它的流程图如下所示:
伙伴系统框架
这里框图介绍了buffered_rmqueue的实现,从它的命名可以看出,它是带有buffer功能的,为什么要这么操作呢,我们知道伙伴系统这一套算法是有一定复杂度的,如果每次申请都要执行算法扫描一下系统中的内存,那么将会比较耗时,所以加上per cpu高速缓存,当我们申请单个page时,优先从缓存中获取,这样就提升了效率,同时作为per cpu的缓冲,也将提升CPU效率,因为不用频繁进行cache的切换,提升cache命中的概率。

free_page

free page是释放page的接口,它的实现如下图:
伙伴系统

释放过程是申请的逆过程,当释放单个page时,优先释放给per cpu高速缓冲区管理,如果高速缓存内的内存大小超出一个设定值,那么高速缓存也会释放一部分给到伙伴系统,而伙伴系统通过循环执行__free_one_page来释放内存到对应的空闲free_area。

### Linux内核中 `alloc_page` 和 `__get_free_page` 的详细用法及注释 在Linux内核中,内存分配是一个核心功能。`alloc_page` 和 `__get_free_page` 是两个用于内存分配的重要函数。以下是它们的详细用法和注释。 #### 1. `alloc_page` `alloc_page` 是一个高级内存分配函数,用于分配单个页面(通常为4KB大小)。它基于伙伴系统实现,并允许指定分配标志来控制分配行为。 - **函数原型**: ```c struct page *alloc_page(gfp_t gfp_mask); ``` - **参数**: - `gfp_mask`:指定分配标志,定义了分配器的行为。例如,`GFP_KERNEL` 表示可以在睡眠的情况下进行分配[^2]。 - **返回值**: - 成功时返回指向分配页面的 `struct page*`。 - 失败时返回 `NULL`。 - **注释**: ```c /** * alloc_page - 分配单个页面 * @gfp_mask: 分配标志,控制分配器的行为 * * 此函数通过伙伴系统分配一个页面。如果无法满足请求, * 则返回 NULL。gfp_mask 参数决定了分配器是否可以睡眠、 * 是否可以从高内存区域分配等。 * * 返回值: * - 成功:返回指向分配页面的 struct page 指针 * - 失败:返回 NULL */ ``` #### 2. `__get_free_page` `__get_free_page` 是一个较低级别的函数,直接分配单个页面并返回物理地址。 `alloc_page` 不同,它不返回 `struct page*`,而是返回线性地址。 - **函数原型**: ```c unsigned long __get_free_page(gfp_t gfp_mask); ``` - **参数**: - `gfp_mask`: `alloc_page` 相同,指定分配标志。 - **返回值**: - 成功时返回分配页面的线性地址。 - 失败时返回 `0`。 - **注释**: ```c /** * __get_free_page - 分配单个页面并返回线性地址 * @gfp_mask: 分配标志,控制分配器的行为 * * 此函数通过伙伴系统分配一个页面,并返回其线性地址。 * 如果无法满足请求,则返回 0。gfp_mask 参数决定了分配器 * 是否可以睡眠、是否可以从高内存区域分配等。 * * 返回值: * - 成功:返回分配页面的线性地址 * - 失败:返回 0 */ ``` #### 3. 使用示例 以下是一个简单的代码示例,展示了如何使用这两个函数: ```c #include <linux/gfp.h> #include <linux/mm.h> void example_usage(void) { struct page *page; unsigned long addr; // 使用 alloc_page 分配页面 page = alloc_page(GFP_KERNEL); if (page) { // 使用页面 printk("Page allocated at physical address: %lx\n", page_to_pfn(page) << PAGE_SHIFT); __free_page(page); // 释放页面 } // 使用 __get_free_page 分配页面 addr = __get_free_page(GFP_KERNEL); if (addr) { // 使用页面 printk("Page allocated at linear address: %lx\n", addr); free_page(addr); // 释放页面 } } ``` #### 4. 注意事项 - `alloc_page` 返回的是 `struct page*`,需要通过 `page_to_pfn` 或其他函数获取物理地址[^3]。 - `__get_free_page` 返回的是线性地址,适合用于简单场景,但不如 `alloc_page` 灵活。 - 在释放页面时,`alloc_page` 使用 `__free_page`,而 `__get_free_page` 使用 `free_page`。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值