当前位置: 首页 > news >正文

[嵌入式学习] XV6Lab 2025笔记--内存管理(一)--伙伴系统

设计目标

  1. 实现伙伴系统
  2. 实现2MB大页映射
  3. 引入COW机制

三者的关系:

  • 大页需要底层的物理内存分配器能够按2MB对齐的连续物理块
  • COW需要物理内存分配器能够追踪物理页的引用计数
  • 当共享的对象是大页,COW需要支持在必要时拆分大页的问题

伙伴系统

原始的XV6物理页分配按照4KB为粒度,但是并不适合大页的分配。因此需要设计一个能够按需分配2MB对齐的连续物理块的分配器,因此想到参考Linux的伙伴系统。

伙伴系统的机制:
当申请特定大小内存块时,优先匹配对应阶数的链表;
若无可用的块则向上拆分高阶块;
释放时合并相邻的空闲块。

核心数据结构与接口

数据结构:

/* 页元数据 */
struct page {int ref;				//当前物理页引用计数uchar order;		//当前块对应的阶uchar is_free;	//是否在freelist中uchar usable;		//是否可以被分配器管理
};
/* 按照Order来维护空闲页链表 */
struct {struct spinlock lock;struct run *freelist[BUDDY_MAX_ORDER + 1];
} kmem;
/* 全局的物理页元数据 */
static struct page pages[KMEM_NPAGE];

新增接口:

void*           kalloc_order(int);
void            kfree_order(void *, int);
void            kaddref_order(void *, int);//用于在COW场景下一整块共享页的引用计数整体增加
void            ksplit_order(void *, int, int);//用于COW与大页的拆分情况
void            kaddref(void *);
int             kgetref(void *);

分配链路

//暴露的接口,用于分配order阶的连续物理页
void *
kalloc_order(int order)
{void *pa;int idx;int i;int npage;acquire(&kmem.lock);pa = alloc_block(order);//实际的分配if(pa){//分配成功,开始按页修改元数据:增加引用计数,设置非free,设置阶数idx = pa_index((uint64)pa);npage = 1 << order;for(i = 0; i < npage; i++){pages[idx + i].ref = 1;pages[idx + i].is_free = 0;pages[idx + i].order = order;}}release(&kmem.lock);if(pa)memset(pa, 5, PGSIZE << order); // fill with junkreturn pa;
}
//找到一片按2^order*4KB对齐的连续物理页
static void *
alloc_block(int order)
{int o;int idx;struct run *r;if(!valid_order(order))panic("kalloc_order");
//从低阶往高阶找是否有足够空间for(o = order; o <= BUDDY_MAX_ORDER; o++){r = kmem.freelist[o];if(r)break;}if(o > BUDDY_MAX_ORDER)return 0;idx = pa_index((uint64)r);kmem.freelist[o] = r->next;pages[idx].is_free = 0;//TODO:可能多余了?
//如果拿到的空间大于实际需要的,则一阶一阶往下打碎while(o > order){o--;push_block(idx + (1 << o), o);}pages[idx].order = order;pages[idx].is_free = 0;return (void*)index_pa(idx);
}
//辅助函数,把对应页开始的order阶的空间放入对应的freelist中
static void
push_block(int idx, int order)
{struct run *r;pages[idx].order = order;pages[idx].is_free = 1;r = (struct run*)index_pa(idx);r->next = kmem.freelist[order];kmem.freelist[order] = r;
}

释放链路

//暴露的接口,用于释放order阶的内存
void
kfree_order(void *pa, int order)
{int idx;int i;int npage;int ref;if(!valid_order(order) || ((uint64)pa % (PGSIZE << order)) != 0)panic("kfree_order");if(!valid_page((uint64)pa))panic("kfree_order");idx = pa_index((uint64)pa);npage = 1 << order;if(idx + npage > KMEM_NPAGE)panic("kfree_order");acquire(&kmem.lock);if(pages[idx].is_free || pages[idx].order != order)panic("kfree_order");ref = pages[idx].ref;//并不直接释放,只是减少引用计数(为了适配COW机制)for(i = 0; i < npage; i++){if(!pages[idx + i].usable || pages[idx + i].order != order ||pages[idx + i].ref != ref || ref < 1)panic("kfree_order");pages[idx + i].ref--;}if(ref > 1){//如果引用计数大于1则直接返回,否则才真的释放release(&kmem.lock);return;}memset(pa, 1, PGSIZE << order);pages[idx].order = order;free_block(idx, order);release(&kmem.lock);
}
//释放内存块并自动向上合并
static void
free_block(int idx, int order)
{int buddy;while(order < BUDDY_MAX_ORDER){//合并buddy = idx ^ (1 << order); //MAGIC:用于找到伙伴的索引if(buddy < 0 || buddy >= KMEM_NPAGE)break;if(!pages[buddy].usable || !pages[buddy].is_free || pages[buddy].order != order)break;remove_block(buddy, order);//从freelist中移除if(buddy < idx)//小的idx = buddy;order++;}push_block(idx, order);//把合并结束的放到对应的freelist中
}
//把buddy从对应的freelist中移除
static void
remove_block(int idx, int order)
{struct run **p;struct run *r;p = &kmem.freelist[order];r = (struct run*)index_pa(idx);while(*p){if(*p == r){*p = r->next;pages[idx].is_free = 0;return;}p = &(*p)->next;}panic("buddy remove");
}
http://www.jsqmd.com/news/746783/

相关文章:

  • 终极指南:5分钟掌握BOTW存档编辑神器
  • 5分钟彻底解放双手:鸣潮自动化工具终极指南,让重复剧情成为过去式
  • 类型即文档,类型即契约:Python 3.15新增@dataclass_transform与ParamSpec组合技,打造自解释API的4步法(内部团队已禁用旧注解)
  • 2026年建筑学论文降AI工具推荐:城市规划建筑设计研究亲测达标完整方案
  • 终极免费Book118文档下载器:如何一键获取完整PDF文档
  • Habitus:声明式容器镜像构建与发布工作流引擎实践指南
  • 解锁你的数字记忆宝库:用WeChatMsg重塑聊天记录的价值
  • 2026 年南京豆包推广合规方案与实施路径:白帽 GEO 优化成主流 - 小艾信息发布
  • 三步开启本地弹幕视频新时代:BiliLocal终极使用指南
  • 单页图床+最新完整版图床系统修复版
  • 使用 OpenClaw 配置 Taotoken 作为其 OpenAI 兼容后端的快速方法
  • 别再为iOS真机调试发愁了!手把手教你用爱思助手给HBuilderX基座签名(附常见错误码44/45解决方案)
  • 带简易后台管理的米表系统 域名出售系统 自适应页面
  • LeRobot端到端机器人学习架构解析:解决具身智能落地的工程挑战
  • 大模型时代,普通人最该掌握的3项核心能力
  • 揭秘AI教材编写技巧!利用AI写教材,一键搞定低查重的专业教材生成
  • CSDNBlogDownloader高效指南:三步实现技术博客完整备份的实用方案
  • MATLAB绘图进阶:手把手教你用网格线优化数据可视化(附代码)
  • 从目标检测到行为识别:YOLO 模型微调实战
  • vLLM 全部8种部署方式(按从简单到企业级排序,附适用场景+最简命令)
  • 为OpenClaw智能体工作流配置Taotoken作为底层模型服务
  • 开源S7-1500驱动实现Niagara 4与西门子PLC高效数据集成
  • 终极指南:如何在本地电脑快速部署AI大模型?llama-cpp-python完整教程
  • 行业内裸眼3D手机膜品牌口碑
  • RedisMe vs TinyRDM vs AnotherRDM
  • 告别重复点击!《鸣潮》自动化助手终极指南:从萌新到高手的完整教程
  • 终极Nintendo Switch NAND管理实战:NxNandManager深度解析
  • Python量化回测慢如蜗牛?3行代码提速300%,资深量化架构师亲授编译级优化秘方
  • 智能APK安装革命:告别臃肿模拟器的Windows安卓应用安装方案
  • 使用Opyrator快速构建机器学习模型交互界面:从Python函数到Web应用