news 2026/10/20 21:03:59

page_alloc make_alloc_exact

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
page_alloc make_alloc_exact

make_alloc_exact()是alloc_pages_exact()的核心实现函数,用于将伙伴系统分配的高阶页块中多余的部分释放掉,只保留调用方实际需要的大小。

核心作用与设计动机

alloc_pages_exact(size)的目标是:分配至少size字节的内存,但尽量不浪费——如果size只需要 3KB,它不会返回一个完整的 4KB 页,而是先分配一个足够大的高阶块,再把多余部分释放回伙伴系统。

这与__get_free_pages(gfp, order)不同:后者按 2 的幂次分配,可能浪费大量内存(如需要 5KB 却分配 8KB)。alloc_pages_exact()通过“先多分、再退还”来减少浪费。

核心实现

static void *make_alloc_exact(unsigned long addr, unsigned int order, size_t size) { if (addr) { unsigned long alloc_end = addr + (PAGE_SIZE << order); unsigned long used = addr + PAGE_ALIGN(size); split_page(virt_to_page((void *)addr), order); while (used < alloc_end) { free_page(used); used += PAGE_SIZE; } } return (void *)addr; }

逐步说明

1.addr检查

if (addr) {

如果分配失败(addr == 0),直接返回 0,不做任何处理。

2. 计算边界

unsigned long alloc_end = addr + (PAGE_SIZE << order); unsigned long used = addr + PAGE_ALIGN(size);
  • alloc_end:整个高阶块的结束地址(addr + 2^order * PAGE_SIZE)。

  • used:实际需要的结束地址(addr + 向上取整到页边界的 size)。

  • PAGE_ALIGN(size)确保used按页对齐,因为释放必须以页为单位。

3. 分裂页块

split_page(virt_to_page((void *)addr), order);

前面讲过的split_page():把一个非复合高阶页拆成2^order个独立的单页,每个页引用计数设为 1。这是关键步骤——只有拆成独立单页,才能单独释放其中一部分。

4. 释放多余页

while (used < alloc_end) { free_page(used); used += PAGE_SIZE; }
  • 从used开始,逐页释放到alloc_end。

  • 每次释放一页(free_page()是free_pages(addr, 0)的简写)。

  • 保留[addr, used)范围内的页给调用方。

5. 返回起始地址

return (void *)addr;

返回分配到的内存起始虚拟地址。

在分配路径中的位置

它被alloc_pages_exact()调用:

void *alloc_pages_exact(size_t size, gfp_t gfp_mask) { unsigned int order = get_order(size); unsigned long addr; addr = __get_free_pages(gfp_mask, order); return make_alloc_exact(addr, order, size); } EXPORT_SYMBOL(alloc_pages_exact);

流程:

  1. get_order(size):计算满足size所需的最小阶数。

  2. __get_free_pages():分配该阶数的页块,返回虚拟地址。

  3. make_alloc_exact():把多余部分释放,只保留实际需要的。

关键约束

1. 必须用free_pages_exact()释放

因为make_alloc_exact()调用了split_page(),页块已被拆成独立单页,不能再作为一个整体释放。必须用配套的free_pages_exact():

void free_pages_exact(void *virt, size_t size) { unsigned long addr = (unsigned long)virt; unsigned long end = addr + PAGE_ALIGN(size); while (addr < end) { free_page(addr); addr += PAGE_SIZE; } } EXPORT_SYMBOL(free_pages_exact);

它逐页释放[virt, virt + PAGE_ALIGN(size))范围。

2. 不能用于复合页

split_page()要求页是非复合的。alloc_pages_exact()内部用__get_free_pages()分配,不带__GFP_COMP,所以页是非复合的,可以安全split_page()。

3. 只适用于低阶

split_page()对高阶页有效,但alloc_pages_exact()通常用于较小的分配(几 KB 到几十 KB)。极大分配(如 MB 级)用alloc_pages_exact()会频繁分裂/释放,效率不高。

总结

make_alloc_exact()是alloc_pages_exact()的精确分配核心:它接收一个高阶页块的虚拟地址,计算实际需要的页数,通过split_page()把页块拆成独立单页,然后逐页释放多余部分,只保留调用方实际需要的内存。它通过“先多分、再退还”的策略减少内存浪费,但代价是必须用配套的free_pages_exact()逐页释放。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 9:27:54

mangopi(芒果派)MQ-R T113 Buildroot开启USB虚拟网卡(RNDIS)并使用SSH调试

前言 在之前的文章中&#xff0c;介绍了用 Buildroot 构建芒果派 MQ-R T113 的固件&#xff0c;以及开启 ADB 调试。 本文继续介绍 SSH 调试所需的开发板配置。 SSH 服务可以实现远程登录、端口转发和文件传输。相比 ADB&#xff0c;它的使用体验更接近默认的 UART 调试方式。…

作者头像 李华
网站建设 2026/10/21 12:39:36

研究生开题答辩前必看:研究局限预判清单,答问环节不慌

开题答辩的提问环节&#xff0c;研究局限几乎躲不开。临场翻遍脑中材料&#xff0c;常常来不及把话说圆&#xff0c;不如提前把边界、成因与补救路径整理成能复述的答案。下面按三类追问场景给出准备动作&#xff0c;并附一张速查表&#xff1b;工具侧可先用知学术AIPaperGPT&a…

作者头像 李华
网站建设 2026/10/20 13:27:18

DDIC Structure 不只是字段集合,从语义属性看 ABAP Dictionary 的建模能力

在 SE11 里创建一个 Structure,看上去很像是在做一件非常机械的事情。建立一个结构名,填 Short Description,在 Components 页签里一行一行加入字段,再给字段绑定 Data Element 或直接指定 Data Type。很多 ABAP 开发人员长期使用 DDIC Structure,也很容易形成一种印象,S…

作者头像 李华
网站建设 2026/10/20 22:26:10

Android 计算器开发实战:从界面设计到核心逻辑

摘要:本文手把手带你从零构建一个基于 Android 的计算器应用。内容涵盖项目环境搭建与分层架构规划、界面布局与组件设计、核心状态变量管理、按钮事件监听、屏幕显示更新、输入合法性校验、用户提示机制,以及独立计算类的封装与联调测试。通过完整流程,你将掌握 Android 开…

作者头像 李华