操作系统实验报告:
一.可变分区存储管理系统模拟
1.实验名称:
可变分区存储管理系统模拟
2.实验目的:
可变分区分配是一种重要的存储管理思想,目前流行的操作系统采用的分段存储管理的基本思想就源自该方法。本实验的目的是通过编程来模拟一个简单的可变分区分配存储管理系统,经过实验者亲自动手编写管理程序,可以进一步加深对可变分区分配存储管理方案设计思想的理解。
3.实验原理:
固定分区分配按操作系统初始化时划定的分区方案为作业分配内存,由于各分区的位置和大小固定,因此作业所需的内存大小通常小于分到的实际内存的大小,造成存储空间的浪费。可变分区分配对此作了改进,它总是根据作业的实际需要分配刚好够用的连续存储空间,保证分配给作业的存储空间都是有用的,避免了零头的产生。
4.仪器与材料:
PC机,Devc++。
5.0实验步骤(老师给的代码):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX 640
struct node // 定义分区
{
int address, size;
struct node *next;
};
typedef struct node RECT;
/*-----函数定义-------*/
RECT *assignment(RECT *head, int application); // 分配分区
void firstfit(RECT *head, RECT *heada, RECT *back1); // 针对首次适应分配算法回收分区
void bestfit(RECT *head, RECT *back1); // 针对最佳适应分配算法回收分区,待扩充
int backcheck(RECT *head, RECT *back1); // 合法性检查
void print(RECT *head); // 输出已分配分区表或空闲分区
/*-----变量定义-------*/
RECT *head, *heada, *back, *assign1, *p;
int application1, maxblocknum;
char way; // 用于定义分配方式:首先适应、最佳适应、最差适应等,目前未使用
int main()
{
char choose;
int check;
RECT *allocated;
head = (RECT *)malloc(sizeof(RECT)); // 建立空闲分区表的初始状态
p = (RECT *)malloc(sizeof(RECT));
head->size = MAX;
head->address = 0;
head->next = p;
maxblocknum = 1;
p->size = MAX;
p->address = 0;
p->next = NULL;
print(head); // 输出空闲分区表的初始状态
// printf("Enter the allocation way (best or first (b/f))\n");
// scanf("%c",&way);
heada = (RECT *)malloc(sizeof(RECT)); // 建立已分配分区表的初始状态
heada->size = 0;
heada->address = 0;
heada->next = NULL;
// print(heada); //输出空闲分区表的初始状态
way = 'f';
do
{
printf("Enter the allocate or reclaim (a/r),or press other key to exit.\n");
scanf(" %c", &choose); // 选择分配或回收
if (tolower(choose) == 'a') // a为分配
{
printf("Input application:\n");
scanf("%d", &application1); // 输入申请空间大小
assign1 = assignment(head, application1); // 调用分配函数分配内存
if (assign1->address == -1) // 分配不成功
printf("Too large application! Allocation fails! \n\n");
else // 分配成功
printf("Allocation Success! ADDRESS=%5d\n", assign1->address);
printf("\n*********Unallocated Table*************\n");
print(head); // 输出
printf("\n*********Allocated Table*** **********\n");
print(heada);
}
else if (tolower(choose) == 'r') // 回收内存
{
back = (RECT *)malloc(sizeof(RECT));
printf("Input address and Size:\n");
scanf("%d%d", &back->address, &back->size); // 输入回收地址和大小
check = backcheck(head, back);
if (check == 1)
{
if (tolower(way) == 'f')
firstfit(head, heada, back); // 首先适应算法回收
printf("\n*********Unallocated Table*************\n");
print(head); // 输出
printf("\n*********Allocated Table*** **********\n");
print(heada);
}
}
} while (tolower(choose) == 'a' || tolower(choose) == 'r');
exit(0);
} // main() end.
/*-------内存分配函数,请补充代码-------*/
RECT *assignment(RECT *head, int application)
{
RECT *before, *after, *assign;
int insert;
before = head;
after = head->next;
insert = 0;
while (!insert) // 寻找合适的空闲分区进行分配
{
if ((after == NULL) || (after->size >= application)) // 找到合适的空闲分区
{
if (after == NULL) // 已经是最后一个节点
{
assign = NULL;
insert = 1;
}
else if (after->size > application) // 分配的空间小于当前空闲分区的大小
{
assign = (RECT *)malloc(sizeof(RECT));
assign->address = after->address;
assign->size = application;
assign->next = NULL;
after->address += application;
after->size -= application;
insert = 1;
}
else if (after->size == application) // 分配的空间等于当前空闲分区的大小
{
assign = (RECT *)malloc(sizeof(RECT));
assign->address = after->address;
assign->size = application;
assign->next = NULL;
before->next = after->next;
free(after);
insert = 1;
}
}
else // 继续寻找
{
before = before->next;
after = after->next;
}
}
if (assign == NULL) // 找不到合适的空闲分区进行分配
{
assign = (RECT *)malloc(sizeof(RECT));
assign->address = -1;
assign->size = -1;
assign->next = NULL;
}
else // 将分配的分区加入到已分配分区表中
{
RECT *temp = heada;
while (temp->next != NULL)
{
temp = temp->next;
}
temp->next = assign;
heada->size++;
}
return assign;
}
/*------------------首先适应回收算法------------*/
void firstfit(RECT *head, RECT *heada, RECT *back1)
{
RECT *before, *after, *back2;
int insert, del;
back2 = (RECT *)malloc(sizeof(RECT));
back2->address = back1->address;
back2->size = back1->size;
back2->next = back1->next;
before = head;
after = head->next;
insert = 0;
while (!insert) // 将回收区插入空闲区表
{
if ((after == NULL) || ((back1->address <= after->address) && (back1->address >= before->address)))
{
before->next = back1;
back1->next = after;
insert = 1;
}
else
{
before = before->next;
after = after->next;
}
}
if (back1->address == before->address + before->size) // 与上一块合并
{
before->size = before->size + back1->size;
before->next = back1->next;
free(back1);
back1 = before;
}
if ((after != NULL) && (after->address == back1->address + back1->size)) // 与下一块合并
{
back1->size = back1->size + after->size;
back1->next = after->next;
free(after);
}
if (head->size < back1->size) // 修改最大块值和最大块个数
{
head->size = back1->size;
maxblocknum = 1;
}
else if (head->size == back1->size)
maxblocknum++;
// 修改已分配分区表,删除相应节点
before = heada;
after = heada->next;
del = 0;
// while (after != NULL) {
// if ((after->address == back2->address) && (after->size == back2->size)) {
// before->next = after->next;
// free(after);
// heada->size--;
// del = 1;
// break; // 找到并删除后直接跳出循环
// }
// else {
// before = before->next;
// after = after->next;
// }
// }
while (!del && after != NULL) // 将回收区从已分配分区表中删除
{
if ((after->address == back2->address) && (after->size == back2->size))
{
before->next = after->next;
free(after);
del = 1;
}
else
{
before = before->next;
after = after->next;
}
}
heada->size--;
}
/*-----------------打印输出链表--------------*/
void print(RECT *output)
{
RECT *before;
int index;
before = output->next;
index = 0;
if (output->next == NULL)
printf("NO part for print!\n");
else
{
printf("index****address****end*****size**** \n");
while (before != NULL)
{
printf("------------------------------------\n");
printf(" %-9d%- 9d%- 9d%- 9d\n", index, before->address, before->address + before->size - 1, before->size);
printf("-------------------------------------\n");
index++;
before = before->next;
}
}
}
/*检查回收块到合法性,back1为要回收到节点地址*/
int backcheck(RECT *head, RECT *back1)
{
RECT *before;
int check = 1;
if (back1->address < 0 || back1->size < 0)
check = 0; // 地址和大小不能为负数
before = head->next;
while ((before != NULL) && check) // 地址不能和空闲区表中节点出现重叠
if (((back1->address < before->address) && (back1->address + back1->size > before->address)) || ((back1->address >= before->address) && (back1->address < before->address + before->size)))
check = 0;
else
before = before->next;
if (check == 0)
printf("Error input!\n");
return check;
}
5.1实验步骤(优化代码):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define MAX 640
typedef struct node {
int address, size;
struct node *next;
} RECT;
RECT *head, *heada;
void print(RECT *output) {
RECT *before = output->next;
int index = 0;
printf("索引****地址****结束*****大小**** \n");
while (before != NULL) {
printf("------------------------------------\n");
printf(" %-9d%- 9d%- 9d%- 9d\n", index, before->address, before->address + before->size - 1, before->size);
printf("-------------------------------------\n");
index++;
before = before->next;
}
}
RECT *allocate(RECT *head, int application) {
RECT *before = head, *after = head->next;
while (after != NULL && after->size < application) {
before = after;
after = after->next;
}
if (after == NULL) {
printf("应用程序太大!分配失败! \n\n");
return NULL;
}
RECT *assign = (RECT*)malloc(sizeof(RECT));
assign->address = after->address;
assign->size = application;
assign->next = NULL;
if (after->size > application) {
after->address += application;
after->size -= application;
} else {
before->next = after->next;
free(after);
}
RECT *temp = heada;
while (temp->next != NULL) {
temp = temp->next;
}
temp->next = assign;
heada->size++;
return assign;
}
void deallocate(RECT *head, RECT *back) {
RECT *before = head, *after = head->next;
while (after != NULL && back->address > after->address) {
before = after;
after = after->next;
}
before->next = back;
back->next = after;
if (back->address == before->address + before->size) {
before->size += back->size;
before->next = back->next;
free(back);
back = before;
}
if (after != NULL && after->address == back->address + back->size) {
back->size += after->size;
back->next = after->next;
free(after);
}
if (head->size < back->size) {
head->size = back->size;
}
RECT *temp = heada, *prev = NULL;
while (temp != NULL && temp->address != back->address) {
prev = temp;
temp = temp->next;
}
if (temp != NULL) {
if (prev == NULL) {
heada = temp->next;
} else {
prev->next = temp->next;
}
free(temp);
heada->size--;
}
}
int main() {
head = (RECT*)malloc(sizeof(RECT));
head->size = MAX;
head->address = 0;
head->next = (RECT*)malloc(sizeof(RECT));
head->next->size = MAX;
head->next->address = 0;
head->next->next = NULL;
heada = (RECT*)malloc(sizeof(RECT));
heada->size = 0;
heada->address = 0;
heada->next = NULL;
print(head);
print(heada);
char choose;
int application1;
do {
printf("输入分配或回收 (a/r),或按任何其他键退出.\n");
scanf(" %c", &choose);
if (tolower(choose) == 'a') {
printf("输入应用程序:\n");
scanf("%d", &application1);
RECT *assign1 = allocate(head, application1);
if (assign1 != NULL) {
printf("分配成功!地址=%5d\n", assign1->address);
}
printf("\n*********未分配的表*************\n");
print(head);
printf("\n*********分配的表*** **********\n");
print(heada);
} else if (tolower(choose) == 'r') {
RECT *back = (RECT*)malloc(sizeof(RECT));
printf("输入地址和大小:\n");
scanf("%d%d", &back->address, &back->size);
if (back->address < 0 || back->size < 0) {
printf("输入错误!\n");
} else {
deallocate(head, back);
}
printf("\n*********未分配的表*************\n");
print(head);
printf("\n*********分配的表*** **********\n");
print(heada);
}
} while (tolower(choose) == 'a' || tolower(choose) == 'r');
free(head->next);
free(head);
free(heada);
return 0;
}
6.问题即讨论
数据结构定义:
使用了结构体 RECT 来表示内存块,其中包含了地址、大小和指向下一个节点的指针。这样的设计使得链表可以方便地管理内存块。
全局变量:
head 和 heada 分别表示未分配和已分配链表的头节点,是全局变量,这样它们可以在各个函数之间传递而无需作为参数传递。
print 函数:
用于打印链表的函数,清晰地显示了索引、地址、结束地址、大小等信息,便于用户了解当前内存块的分布情况。
allocate 函数:
用于分配内存,根据传入的应用程序大小在未分配链表中找到合适的位置,然后创建一个新的节点表示已分配内存。如果找不到合适的位置,会输出相应的错误信息。
deallocate 函数:
用于回收内存,根据传入的回收块的地址和大小,在未分配链表中找到合适的位置进行插入,然后合并相邻的未分配块,同时更新已分配链表。
主函数(main):
初始化了未分配和已分配链表的头节点,然后通过循环接受用户输入,根据用户选择进行内存分配或回收。用户输入 ‘a’ 进行内存分配,输入 ‘r’ 进行内存回收,其他键退出循环。
释放内存:
在程序结束时,释放了动态分配的内存,包括未分配链表和已分配链表的头节点。
改进空间:
当前代码在简单模拟内存分配回收的过程中比较清晰,但在真实应用中可能需要考虑更多的情况,例如内存碎片问题、线程安全性等。对于更复杂的应用场景,可能需要更完善的内存管理算法。