实验二:存储管理动态分区分配及回收算法
一、实验目的
分区管理是内存管理中广泛应用的一种技术,本实验要求使用结构化的高级语言(C/C++)模拟实现动态分区分配算法和回收算法。通过编写 First Fit 算法、Best Fit 算法以及空闲区回收算法,帮助理解不同分配算法的特点,并加深对内存管理的掌握。
二、实验要求
First Fit 分配算法:
实现 First Fit 分配算法,即从空闲区队列中寻找第一个足够大的空闲分区进行分配。
Best Fit 分配算法:
实现 Best Fit 分配算法,即从空闲区队列中寻找最适合的空闲分区进行分配,即剩余空间最小的空闲区。
空闲区回收算法:
实现空闲区回收算法,支持分配后回收内存区域,并将回收的内存区域正确地合并回空闲区队列。
三、实验过程
1.准备
A. 查阅相关资料主要内容包括:
内存管理原理:了解操作系统中内存的分配方式,特别是动态分区分配方法。
分配算法:深入理解 First Fit 和 Best Fit 分配算法的工作原理,优缺点,以及如何在内存管理中应用这些算法。
空闲区合并:了解如何处理内存回收时的空闲区合并问题,避免内存碎片。
C语言链表操作:学习如何在 C 语言中使用链表数据结构,管理空闲区。
B. 初步编写程序:
定义数据结构:定义 Node 结构体,用于表示每个内存分区,包括分区的起始地址、大小、指向下一个分区的指针。
实现空闲区链表的初始化:通过链表管理空闲内存区,初始时创建一块大内存区域,设置首地址为 0,大小为 32767。
实现分配算法:编写 First Fit 和 Best Fit 分配算法的具体实现,确保能够根据用户输入的分区大小进行分配。
实现回收算法:设计回收算法,能够在内存回收后合并相邻的空闲块。
打印空闲区链表的状态:每执行一次分配或回收操作后,打印当前空闲区链表的状态。
C. 准备测试数据:
初始空闲区的测试:测试在程序启动时,初始化一块空闲内存区域是否正确。
分配算法的测试:设计不同的申请内存大小,测试 First Fit 和 Best Fit 算法的表现,验证内存是否按照算法要求分配。
回收操作的测试:模拟回收内存并合并相邻空闲块,验证回收和合并功能是否正常工作。
边界测试:设计极端测试案例,如内存申请大小为 0、内存回收超出范围、空闲区链表为空时进行分配等,确保程序能够处理各种边界情况。
2.上机调试
- 主要流程和源代码
初始化一个空闲区,首地址为 0,大小为 32767。
提示用户选择分配算法:First Fit 或 Best Fit。
提示用户选择是进行分配还是回收操作。
如果是分配操作,要求输入申请区的大小;如果是回收操作,要求输入释放区的首地址和大小。
每执行一次分配或回收操作,输出空闲区队列的状态。
#include <stdio.h>
#include <stdlib.h>
// 分区描述器
typedef struct node {
int adr; // 分区起始地址
int size; // 分区大小
struct node *next; // 下一个分区
} Node;
// 全局指针
Node *head1 = NULL; // 空闲区队列头
Node *assign = NULL; // 指向申请到的分区
int free_size; // 用户申请大小
// 函数声明
void init_free_area(int total_size);
void print_free_list();
int check_release(int adr, int size);
void first_fit_allocate();
void best_fit_allocate();
void first_fit_release();
void best_fit_release();
void sort_free_list_by_addr();
// 主程序
int main() {
int choice, op;
init_free_area(32767); // 初始一块 0~32766 大小的空闲区
while (1) {
printf("\n请选择分配算法:1-First Fit 2-Best Fit 0-退出 :");
scanf("%d", &choice);
if (choice == 0) break;
printf("1-分配 2-回收 :");
scanf("%d", &op);
if (op == 1) {
printf("输入申请分区大小:");
scanf("%d", &free_size);
if (choice == 1) first_fit_allocate();
else best_fit_allocate();
} else if (op == 2) {
int adr, size;
printf("输入释放分区首址和大小:");
scanf("%d %d", &adr, &size);
if (!check_release(adr, size)) {
printf("释放区不合法,跳过。\n");
} else {
free_size = size;
// 先将这块释放区插入到空闲链表
Node *p = (Node *)malloc(sizeof(Node));
p->adr = adr;
p->size = size;
p->next = head1;
head1 = p;
// 再不同算法合并相邻空闲块
if (choice == 1) first_fit_release();
else best_fit_release();
}
} else {
printf("无效操作!\n");
}
print_free_list();
}
return 0;
}
// 初始化空闲区链表
void init_free_area(int total_size) {
head1 = (Node *)malloc(sizeof(Node));
head1->adr = 0;
head1->size = total_size;
head1->next = NULL;
printf("初始化空闲区链表,总大小 %d\n", total_size);
print_free_list();
}
// 打印空闲区链表
void print_free_list() {
printf("\n空闲区队列情况:\n");
printf("编号\t首址\t终址\t大小\n");
Node *p = head1;
int idx = 1;
while (p) {
printf("%d\t%d\t%d\t%d\n", idx, p->adr, p->adr + p->size - 1, p->size);
p = p->next; idx++;
}
}
// 检查释放区合法性(简单示例:首址>=0 且 size>0)
int check_release(int adr, int size) {
if (adr < 0 || size <= 0) return 0;
return 1;
}
// First Fit 分配
void first_fit_allocate() {
Node *p = head1, *prev = NULL;
while (p) {
if (p->size >= free_size) {
// 找到首个满足的块
assign = p;
break;
}
prev = p;
p = p->next;
}
if (!p) {
printf("没有合适的空闲区,无法分配!\n");
return;
}
printf("分配:起始地址 %d,大小 %d\n", assign->adr, free_size);
assign->adr += free_size;
assign->size -= free_size;
if (assign->size == 0) {
// 整块用完,从链表中移除
if (prev) prev->next = assign->next;
else head1 = assign->next;
free(assign);
}
}
// Best Fit 分配
void best_fit_allocate() {
Node *p = head1, *prev = NULL;
Node *best = NULL, *best_prev = NULL;
while (p) {
if (p->size >= free_size) {
if (!best || p->size < best->size) {
best = p;
best_prev = prev;
}
}
prev = p;
p = p->next;
}
if (!best) {
printf("没有合适的空闲区,无法分配!\n");
return;
}
printf("分配:起始地址 %d,大小 %d\n", best->adr, free_size);
best->adr += free_size;
best->size -= free_size;
if (best->size == 0) {
if (best_prev) best_prev->next = best->next;
else head1 = best->next;
free(best);
}
}
// First Fit 回收(合并相邻空闲区)
void first_fit_release() {
sort_free_list_by_addr();
Node *p = head1;
while (p && p->next) {
if (p->adr + p->size == p->next->adr) {
// 相邻,合并
p->size += p->next->size;
Node *tmp = p->next;
p->next = tmp->next;
free(tmp);
} else {
p = p->next;
}
}
}
// Best Fit 回收(逻辑同 First Fit,保留按大小查找特性,可以不排序)
void best_fit_release() {
// 回收时同样需要按地址合并,否则会产生碎片
first_fit_release();
}
// 按首址升序排序空闲链表(冒泡法)
void sort_free_list_by_addr() {
if (!head1 || !head1->next) return;
int swapped;
do {
swapped = 0;
Node **pp = &head1;
while ((*pp)->next) {
if ((*pp)->adr > (*pp)->next->adr) {
// 交换节点指针
Node *tmp = *pp;
*pp = tmp->next;
tmp->next = (*pp)->next;
(*pp)->next = tmp;
swapped = 1;
}
pp = &((*pp)->next);
}
} while (swapped);
}
4.遇到的主要问题和解决方法
A. 空闲区回收算法中的内存合并问题
在回收内存时,特别是在first_fit_release和best_fit_release中,我们尝试将相邻的空闲块合并。这个过程有时可能导致不正确的合并,尤其是在多个释放区之间的地址顺序不完全连续时。
解决方法:
排序:为了确保释放的区块能够与相邻的空闲区合并,我们首先需要对空闲区链表进行排序,按照首地址升序排列。这样可以确保释放的区块能够与其相邻的块合并。
逐个检查:通过遍历整个空闲区链表,逐个检查是否存在相邻的块,如果发现两个块是相邻的(一个块的终址等于另一个块的首址),就进行合并操作。
B. 分配算法中的空闲区选择问题
在first_fit_allocate和best_fit_allocate中,如何选择合适的空闲区是一个关键问题。对于First Fit,我们从头开始查找第一个足够大的块进行分配;而Best Fit则寻找剩余空间最小的块。
解决方法:
First Fit:尽量避免遍历不必要的空闲区链表,如果找到合适的块就立刻分配。
Best Fit:为避免碎片过多,可以在分配前对空闲区进行合并,减少碎片的产生。
C. 空闲区打印时的输出格式问题
在输出空闲区链表时,可能会遇到格式显示不清楚的问题,特别是在分区大小很大或链表很长时,输出的结果可能会显得杂乱。
解决方法:
确保输出时对齐,并使用适当的格式化控制符来美化输出,使其更易读。
四、实验结果
五、实验总结

浙公网安备 33010602011771号