1. 程式人生 > >計算機作業系統_記憶體管理

計算機作業系統_記憶體管理

記憶體管理

設計程式模擬記憶體的動態分割槽記憶體管理方法。記憶體空閒區使用空閒分割槽表進行管理,採用最先適應演算法從空閒分割槽表中尋找空閒區進行分配,記憶體回收時不考慮與相鄰空閒區的合併。
假定系統的記憶體共640K,初始狀態為作業系統本身佔用40K。

t1 時刻,為作業A、B、C分配80K、60K、100K、的記憶體空間;
t2 時刻作業B完成;
t3 時刻為作業D分配50K的記憶體空間;
t4 時刻作業C、A完成;
t5 時刻作業D完成。

要求程式設計序分別輸出t1、t2、t3、t4、t5時刻記憶體的空閒區的狀態。

流程圖:
在這裡插入圖片描述

資料結構定義:
//程序PCB型別的描述
struct PCB
{
char name; //程序名
int address; //程序分割槽起止
int len; //程序所佔分割槽長度
};
struct PCB PCBelem[maxPCB];//程序佔用記憶體表
//分割槽型別的描述
struct Part
{
int address; //空閒分割槽起始地址
int len; //空閒分割槽大小
};
struct Part Partelem[maxPart]; //空閒分割槽表

主要變數說明:
int length = 640 ; //系統有 640 KB 的空閒
int fnum = 0; //記錄總的分割槽數量
int jnum = 0; //記錄總的程序數量
int leng = 0; //臨時變數

函式說明:
void init4IOS(int tem) //為作業系統分配40k記憶體
int getTagByPcb(char name) //判定輸入的程序是否存在以及位置
void request() //程序分配請求
void getPrint() //列印空閒分割槽
void jcPrintf() //列印程序
void release() //回收指定程序記憶體

關鍵點敘述:

(1) 記憶體結構的建立及表示


在這裡插入圖片描述
分別建立程序佔用記憶體表和空閒分割槽表,由於程序和空閒分割槽的所有屬性值不一樣,所以需要分別建表,它們之間起始地址是連續的,邏輯上形成一整塊記憶體。

(2) 程序請求分配
在這裡插入圖片描述
當程序提出記憶體分配請求以後,首先需要做的是在空閒分割槽表當中尋找一個單獨的,足夠請求程序分配的記憶體空間。注意,尋求該空間需要從記憶體低址開始尋找,這樣有利於保護高址大塊記憶體,當然,缺點就是會產生較多的碎片記憶體,這些記憶體難以被利用。(本程式未涉及相鄰空閒空間的合併,以及記憶體空間的緊湊)若存在這樣一個空間,則從該空間當中劃出請求程序所需的記憶體大小,將該記憶體塊(含有起始地址等資訊的結構體結點)存入程序佔用記憶體表當中,同時,對於被劃分的記憶體空間,需要修改其起始地址,達到邏輯上的合理。值得一提的是,若記憶體分配以後,被劃分記憶體空間大小為0,則需要除去該條記錄(移除(覆蓋)空閒分割槽表)。

(3)回收程序
當對指定程序提出回收要求時,會產生兩個反應,一是會對空閒分割槽表插入一塊記憶體,用以表示被回收記憶體的空閒已空閒出來;二是對於被回收的程序,不應該出現在程序佔用表當中,所以應當將其移除程序佔用表。
對於以上兩個操作的實現:對空閒分割槽表插入空閒記憶體時,需要從高址記憶體空間開始對比插入記憶體空間的大小,當出現記憶體空間的起始地址小於插入記憶體空間的起始地址時停止對比查詢,將其後的記憶體空間均向後移動一位,以騰出一個位置用於插入需要插入的空閒記憶體空間,這樣在物理結構上也就合理了。除去被回收的程序,思想與上類似,找到該程序以後,將該程序以後的結點均向前移動一位,末尾指向相應減一,除去被回收的程序目的在於避免對同一程序進行重複回收。

存在問題
模擬記憶體管理所實現效果簡單,與真實記憶體分配存在很大差異。

改進:
(1) 實現相鄰空閒空間的合併
(2) 記憶體空間緊湊
(3) 其他優化

個人總結:
1、 本程式編寫完成以後,初步理解記憶體管理過程。程式實現的功能較為簡單,沒有考慮相鄰空閒分割槽的合併,以及碎片空間的緊湊整理等操作。
2、越努力,越幸運!_

程式效果圖(部分):
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

完整程式碼:

#include "stdio.h"
#include "Windows.h"

#define  maxPCB 100   //定義最大PCB結點數
#define  maxPart 100  //定義最大分割槽

//程序PCB型別的描述
struct PCB
{
	char name;    //程序名
	int address;  //程序分割槽起止
	int len;      //程序所佔分割槽長度
};
struct PCB PCBelem[maxPCB];//程序佔用記憶體表

//分割槽型別的描述
struct Part
{
	int address;	//空閒分割槽起始地址
	int len;		//空閒分割槽大小
};
struct Part Partelem[maxPart];//空閒分割槽表

int length = 640 ;	//系統有 640 KB 的空閒
int fnum = 0;		//記錄總的分割槽數量
int jnum = 0;		//記錄總的程序數量

struct Part part;	//公共使用臨時結點(分割槽)
struct PCB pcb;		//公共使用臨時結點(程序)

//為作業系統分配40k記憶體
void init4IOS(int tem)
{
	length = length - tem;	//剩餘系統空閒空間減少
	part.address = 0 + 40;	//作業系統佔用,空閒記憶體從40開始
	part.len = length;		//空閒記憶體大小
	Partelem[fnum] = part;	//存入空閒分割槽表
	fnum ++;				//分割槽數增加
}

//判定輸入的程序是否存在以及位置
int getTagByPcb(char name)
{
	int i;
	for(i = 0; i < jnum; i ++)
	{
		if(name == PCBelem[i].name)
		{
			return i;
		}	
	}
	printf("\n\t\t找不到程序名%c,請重新輸入!\n",name);	 
	return -1;
}

//程序分配請求
void request()
{
	char c = 0;
    while(true)
	{
		printf("\n\t\t請輸入請求記憶體程序 名稱:");
		fflush(stdin);//清空緩衝區
		scanf("%c",&pcb.name);
		//檢查是否已存在程序
		for(int j = 0; j < jnum; j++)
		{
			if(PCBelem[j].name == pcb.name)
			{
				printf("\n\t\t程序 %c 已存在,可嘗試輸入其他名稱,也可以先回收該程序!\n",pcb.name);
				return;
			}
		}
		printf("\n\t\t\t\t   長度:");
		fflush(stdin);//清空緩衝區
		scanf("%d",&pcb.len);
		length = length - pcb.len;	//減去相對應的作業系統剩餘空閒空間	
		if(length <= 0)
		{
			if(length == 0)
			{
				printf("\n\t\t警告:系統資源已經全部分配!\n");			
			}
			else
			{
				length = length + pcb.len;	//分配失敗將記憶體換回去,以免溢位
				printf("\n\t\t未找到合適空間或者系統資源不足!\n");	return;
			}
		}
		//如果符合分配條件,進行分配
		for(int i = 0; i < fnum; i++)
		{
			//尋找一個可以分配的空間
			if(pcb.len <= Partelem[i].len)
			{
				//改變程序佔用地址
				pcb.address = Partelem[i].address;
				//儲存該程序
				PCBelem[jnum++] = pcb;

				//對空閒分割槽進行劃分
				Partelem[i].address = Partelem[i].address + pcb.len;
				Partelem[i].len = Partelem[i].len - pcb.len;
				break;//關鍵作用(從低址找到一個空間就可以了,沒必要再往後找了)
			}			
		}
		//除去分配後空閒空間為0的記錄
		if(Partelem[i].len == 0)
		{
			int leng = i;
			//進行前移覆蓋
			while(leng != fnum)
			{
				part.address = Partelem[leng+1].address;
				part.len = Partelem[leng+1].len;
				Partelem[leng] = part;
				leng++;
			}
			//分割槽數減少
			fnum--;
		}
		printf("\n\t\t是否要繼續輸入程序?(Y/y) 是/(N/n) 否:");
	    fflush(stdin);
	    c = getchar(); 
	    fflush(stdin);
		if(c=='N'||c=='n')
		{
			break;
		}
	}
}

//列印空閒分割槽
void getPrint()
{
	printf("\t\t----------------------空閒分割槽 begin----------------------\n");
	int j = 1;
    for (int i = 0;i < fnum; i ++)
    {
	   printf("\n\t\t第%d塊空閒記憶體 起始地址為%d,容量為%d\n",j,Partelem[i].address,Partelem[i].len);
	   j ++;
    }
	printf("\n\t\t----------------------空閒分割槽  end ----------------------\n"); 
}

//列印程序
void jcPrintf()
{
	printf("\n\t\t名稱\t起始地址\t大小\n");
	for(int i = 0 ; i < jnum; i++)
	{
		printf("\n\t\t%2c\t%4d\t\t%d KB\n",PCBelem[i].name,PCBelem[i].address,PCBelem[i].len);
	}
}

//回收指定程序記憶體
void release()
{
	int i = 0;
	char name;
	printf("\n\t\t請輸入想要回收的程序名稱:");
	fflush(stdin);//清空緩衝區
	scanf("%c",&name);
    if(getTagByPcb(name) == -1)
    {
		printf("\n\t\t該程序不存在或者已經被回收!\n");
		return;
    }

	printf("\n\t\t正在回收%c的記憶體:",name);
	for(int j = 0; j < 15; j++)
	{
		printf("▊");
	    Sleep(200);
	}
	printf(" 完成 \n");

	//for迴圈尋找該程序
	for(i = fnum; i >= 0; i --)
	{
		int leng = fnum;
		if(PCBelem[getTagByPcb(name)].address > Partelem[i-1].address || i == 0)
		{
			//while迴圈為該程序騰出一個位置
			while(leng != i)
			{
				part.address = Partelem[leng-1].address;
				part.len = Partelem[leng-1].len;
				Partelem[leng] = part;
				leng--;
			}
			break;//關鍵(從高址往前找到一個空間就可以了,沒必要再往前找了)
		}		
	}
	//系統空閒空間對應增加
	length = length + PCBelem[getTagByPcb(name)].len;
	//使用公共的結點記錄即將產生的空閒空間
	part.address = PCBelem[getTagByPcb(name)].address; 
	part.len = PCBelem[getTagByPcb(name)].len;
	//將該結點存入之前騰出的位置
	Partelem[i] = part;
	//分割槽數增加
	fnum ++;

	//對程序佔用記憶體表進行調整,除去被回收程序
	int leng = getTagByPcb(name);
	//進行前移覆蓋
	while(leng != jnum)
	{
		pcb.name = PCBelem[leng+1].name;
		pcb.address = PCBelem[leng+1].address;
		pcb.len = PCBelem[leng+1].len;
		PCBelem[leng] = pcb;
		leng++;
	}
	//程序數減少
	jnum--;
}

void main()
{
	char tem = 0;
	int OSsize = 40;
	int b = 1, k;
	//為作業系統分配記憶體
	init4IOS(OSsize);
	while (b)
	{
		system("cls");
		printf("\n\n\t\t作業系統記憶體分配\n\n");
		printf("\t\t已為作業系統分配了 40 KB 記憶體\n",tem);
		printf("\n\t\t ----------------------------\n");
		printf("\t\t|1.... 請求分配記憶體           |\n");
		printf("\t\t|2.... 輸出空閒分割槽           |\n");
		printf("\t\t|3.... 強制程序結束           |\n");
		printf("\t\t|4.... 輸出程序資訊           |\n");
		printf("\t\t|0.... 退出                   |\n");
		printf("\t\t ----------------------------\n\n");
		printf("\t\t當前作業系統空閒記憶體:%d KB\n",length);		
		printf("\n\t\t請選擇:");
		fflush(stdin);//清空緩衝區
		scanf("%d", &k);
		switch (k)
		{	
			case 1: request();       break;
			case 2: getPrint();	     break;
			case 3: release();       break;
			case 4: jcPrintf();      break;
			case 0:	b = 0;           break;
			default:printf("\n\t\t輸入無效!\n");break;
		}
		if (b != 0)  { printf("\n\t\t"); system("pause"); }
	}
}

如有錯誤,歡迎指正!