「BUAA OS Lab2-1-Extra」数组模拟伙伴系统
Part 0 前言
本篇博客记录模拟伙伴系统的数组实现方法,Part 1为题目描述,Part 2为设计分析,Part 3为课上源码(已AC并开源)参考,读者可自取所需。
同时,本博客系统已支持评论区,欢迎您参考 Gitment评论区配置与使用中Part2开通您在本博客的权限,留下您的想法。
Part 1 题目描述
请你对现有的物理内存管理机制进行修改,对 MOS 中64MB物理内存的高地址 32MB建立伙伴
系统。下面对本题中所需要实现的伙伴系统进行描述:
内存区间的初始化
伙伴系统将高地址32MB划分为数个内存区间,每个内存区间有两种状态:已分配和未分配。
每个内存区间的大小只可能是4×2iKB,其中i是整数且0≤i≤10。
初始,共有8个4MB大小的内存区间,状态均为未分配。
内存区间的分配
每次通过伙伴系统分配 xB的空间时,找到满足如下三个条件的内存区间:
- 该内存区间的状态为未分配。
- 其大小不小于 xB。
- 满足上面两个条件的前提下,该内存区间的起始地址最小。
如果不存在这样的内存区间,则本次分配失败;否则,执行如下步骤:
- 设该内存区间的大小为 yB,若y2<x或y=4K,则将该内存区间的状态设为已分配,将该内存区间分配并结束此次分配过程。
- 否则,将该内存区间分裂成两个大小相等的内存区间,状态均为未分配。
- 继续选择起始地址更小的那个内存区间,并返回步骤 1。
内存区间的释放
当一个内存区间使用完毕,通过伙伴系统释放时,将其状态设为未分配。
我们称两个内存区间x和y是可合并的,当且仅当它们满足如下四个条件:
- x和y的状态均为未分配。
- x和y是由同一个内存区间一次分裂所产生的两个内存区间。
若存在两个可合并的内存区间,则将两个内存区间合并,若合并后仍存在两个可合并的内存区间,则继续合并,直到不存在两个可合并的内存区间为止。
请你实现如下的三个函数:
初始化函数 buddy_init
- 函数原型为:
void buddy_init(void)
- 调用此函数后,为 MOS 中 64MB物理内存的高地址32MB初始化伙伴系统。初始化结束后,伙伴系统中仅有只有8个4MB的待分配内存区间。
分配函数 buddy_alloc
- 函数原型为:
int buddy_alloc(u_int size, u_int *pa, u_char *pi)
- 调用此函数后,通过伙伴系统分配大小不小于
size
字节的空间,分配逻辑见上述描述。
如果分配失败,返回−1。否则,将pa
指向所分配内存区间的起始地址,设所分配内存区间的大小为4×2iKB,令*pi = i
,并返回0。
,令*pi = i
,并返回0。
释放函数 buddy_free
- 函数原型为:
void buddy_free(u_int pa)
- 调用此函数后,通过伙伴系统释放一个状态为已分配的内存区间,其起始地址为
pa
。释放后的合并逻辑见上述描述。
注意事项
你需要先在include/pmap.h
中加入如下三个函数定义:
1 | void buddy_init(void); |
之后再在mm/pmap.c
中实现这三个函数。
评测逻辑
评测过程中,我们会将所有的Makefile
文件、include.mk
以及 init/init.c
替换为 lab2 初始配置,接着将init/init.c
中的mips_init
函数改为如下形式:
1 | void mips_init(){ |
最后的*((volatile char*)(0xB0000010)) = 0;
会终止 Gxemul 模拟器的运行,避免占用评测资源。
buddy_test
在评测过程中新添加到init/init.c
的函数,其中仅包含以下两种操作:
- 调用
buddy_alloc
函数,我们保证size
不为0。 - 调用
buddy_free
函数,我们保证pa
是之前某次调用buddy_alloc
所得到的。
每调用一个函数算一次操作,我们保证总操作数不超过1000。
运行make
指令的最大时间为10秒,运行Gxemul模拟器的最大时间为4秒。
设伙伴系统管理的物理页数为n,标程中buddy_alloc
和buddy_free
两个函数的时间复杂度均为O(n),请你尽量以此复杂度设计算法。
Part 2 设计分析
基于树的实现
通过题目的描述,我们可以自然地得到树的设计思路:设计TreeNode
结构体为树节点,储存左右儿子(不一定存在)和父亲的指针信息,储存分配信息以及储存空间大小。
受限于上机时间、实现难度和buddy_free
函数时间复杂度的考虑,笔者最终并没有采取这种办法,以下着重介绍第二种思路:基于数组的实现。
基于数组的实现
题目给出,对于给定size
,若其可能被分配,其分配大小只能为4×2iKB,因此,我们以一页(4KB)为单位,将内存分为8×1024个页面,利用数组buddyPage[8 * 1024]
表示。
对于页数2i,我们按照如下公式计算得到:
4×2i−1×1024<size≤4×2i×1024得到页数2i后,由于伙伴系统的特点,我们从下标0开始,以2i为步长,从低至高遍历buddyPage
,若某一步内buddyPage
全部未分配,则将其标记为该次分配并返回;若遍历结束仍未找到满足条件的块,标记为未找到。
对于buddy_free
函数,我们需要释放以给定地址开始的内存,因此,我们在分配时需要区分不同的分配。举例来说,两个相邻的4KB在两次分配中分别被分配,那么当给定第一个4KB的地址时,只能释放第一个4KB。
基于数组的实现保证了我们时间复杂度为O(n),同时正确性可以逻辑证明,兼顾了实现难度和实际性能。
Part 3 课上源码
全局变量与辅助函数
1 | int buddyPage[9999] = {0}; |
buddy_init
1 | void buddy_init(void) |
buddy_alloc
1 | int buddy_alloc(u_int size, u_int *pa, u_char *pi) |
buddy_free
1 | void buddy_free(u_int pa) |
Part 4 后记
Part2中提到了树的实现,笔者不会写树了(菜qwq),并且分析时感觉可能需要递归释放空间,可能会超过O(n)的限制,因此没有继续深究,若读者通过此方法实现或有想法,欢迎和我取得联系一起探讨。
Part3中的代码为上机时最后一次提交,注释掉了一些调试用代码,并且很多地方的细节处理的不够漂亮(可能还有英文单词拼写错误),欢迎批评指正。
This is copyright.