一.目的
1.通過這次實驗,加深對動態分區分配的理解,進一步掌握首次適應算法和最佳適應算法的理解。了 解動態分區分配方式中使用的數據結構和分配算法,進一步加深對動態分區存儲管理方式及其實現 過程的理解。提高學生設計實驗、發現問題、分析問題和解決問題的能力。
2.學會可變式分區管理的原理是在處理作業過程中建立分區,使分區大小正好適合作業的需求。
3.當一個作業執行完成后,作業所占的分區應歸還給系統。
二.原理
首次適應算法
以空閑分區鏈為例來說明采用FF算法時的分配情況。FF算法要求空閑分區鏈以地址遞增的次序鏈接。在分配內存時,從鏈首開始順序查找,直至找到一個大小能滿足要求的分區為止;然后再按照作業的大小,從該分取中划出一塊內存空間分配給請求者,余下的空閑分區仍留在空閑鏈中。若從鏈首直到鏈尾都不能找到一個能滿足要求的分區,則此次內存分配失敗,返回。該算法傾向於優先利用內存中低地址部分的空閑分區,從而保留了高址部分的大空閑區。這給為以后到達的大作業分配大的內存空間創造了條件,其缺點是低址部分不斷被划分,會留下許多難以利用的、很小的空閑分區,而每次查找又都是從低址部分開始,這無疑會增加查找可用空閑分區時的開銷。
最佳適應算法
所謂“最佳”是指每次為作業分配內存時,總是把能滿足要求、又是最小的空閑分區分配給作業,避免“大材小用”。為了加速尋找,該算法要求將所有的空閑分區按其容量以從小到大的順序形成以空閑分區鏈。這樣,第一次找到的能滿足要求的空閑區,必然是最佳的。孤立地看,最佳適應算法似乎是最佳的,然而在宏觀上卻不一定。因為每次分配后所割下來的剩余部分總是最小的,這樣,在存儲器中會留下許多難以利用的開銷。
三.實驗流程圖
首次適用算法
最佳適用算法
四.程序清單(VS 2013)

#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> enum STATE { Free, Busy }; struct subAreaNode { int addr; // 起始地址 int size; // 分區大小 int taskId; // 作業號 STATE state; // 分區狀態 subAreaNode *pre; // 分區前向指針 subAreaNode *nxt; // 分區后向指針 }subHead; // 初始化空閑分區鏈 void intSubArea() { // 分配初始分區內存 subAreaNode *fir = (subAreaNode *)malloc(sizeof(subAreaNode)); // 給首個分區賦值 fir->addr = 0; fir->size = 1000; // 內存初始大小 fir->state = Free; fir->taskId = -1; fir->pre = &subHead; fir->nxt = NULL; // 初始化分區頭部信息 subHead.pre = NULL; subHead.nxt = fir; } // 首次適應算法 int firstFit(int taskId, int size) { subAreaNode *p = subHead.nxt; while (p != NULL) { if (p->state == Free && p->size >= size) { // 找到要分配的空閑分區 if (p->size - size <= 10) { // 整塊分配 p->state = Busy; p->taskId = taskId; } else { // 分配大小為size的區間 subAreaNode *node = (subAreaNode *)malloc(sizeof(subAreaNode)); node->addr = p->addr + size; node->size = p->size - size; node->state = Free; node->taskId = -1; // 修改分區鏈節點指針 node->pre = p; node->nxt = p->nxt; if (p->nxt != NULL) { p->nxt->pre = node; } p->nxt = node; // 分配空閑區間 p->size = size; p->state = Busy; p->taskId = taskId; } printf("內存分配成功!\n"); return 1; } p = p->nxt; } printf("找不到合適的內存分區,分配失敗...\n"); return 0; } // 最佳適應算法 int bestFit(int taskId, int size) { subAreaNode *tar = NULL; int tarSize = 1000 + 1; subAreaNode *p = subHead.nxt; while (p != NULL) { // 尋找最佳空閑區間 if (p->state == Free && p->size >= size && p->size < tarSize) { tar = p; tarSize = p->size; } p = p->nxt; } if (tar != NULL) { // 找到要分配的空閑分區 if (tar->size - size <= 10) { // 整塊分配 tar->state = Busy; tar->taskId = taskId; } else { // 分配大小為size的區間 subAreaNode *node = (subAreaNode *)malloc(sizeof(subAreaNode)); node->addr = tar->addr + size; node->size = tar->size - size; node->state = Free; node->taskId = -1; // 修改分區鏈節點指針 node->pre = tar; node->nxt = tar->nxt; if (tar->nxt != NULL) { tar->nxt->pre = node; } tar->nxt = node; // 分配空閑區間 tar->size = size; tar->state = Busy; tar->taskId = taskId; } printf("內存分配成功!\n"); return 1; } else { printf("找不到合適的內存分區,分配失敗...\n"); return 0; } } int freeSubArea(int taskId) // 回收內存 { int flag = 0; subAreaNode *p = subHead.nxt, *pp; while (p != NULL) { if (p->state == Busy && p->taskId == taskId) { flag = 1; if ((p->pre != &subHead && p->pre->state == Free)&& (p->nxt != NULL && p->nxt->state == Free)) { // 情況1:合並上下兩個分區 // 先合並上區間 pp = p; p = p->pre; p->size += pp->size; p->nxt = pp->nxt; pp->nxt->pre = p; free(pp); // 后合並下區間 pp = p->nxt; p->size += pp->size; p->nxt = pp->nxt; if (pp->nxt != NULL) { pp->nxt->pre = p; } free(pp); } else if ((p->pre == &subHead || p->pre->state == Busy)&& (p->nxt != NULL && p->nxt->state == Free)) { // 情況2:只合並下面的分區 pp = p->nxt; p->size += pp->size; p->state = Free; p->taskId = -1; p->nxt = pp->nxt; if (pp->nxt != NULL) { pp->nxt->pre = p; } free(pp); } else if ((p->pre != &subHead && p->pre->state == Free)&& (p->nxt == NULL || p->nxt->state == Busy)) { // 情況3:只合並上面的分區 pp = p; p = p->pre; p->size += pp->size; p->nxt = pp->nxt; if (pp->nxt != NULL) { pp->nxt->pre = p; } free(pp); } else { // 情況4:上下分區均不用合並 p->state = Free; p->taskId = -1; } } p = p->nxt; } if (flag == 1) { // 回收成功 printf("內存分區回收成功...\n"); return 1; } else { // 找不到目標作業,回收失敗 printf("找不到目標作業,內存分區回收失敗...\n"); return 0; } } // 顯示空閑分區鏈情況 void showSubArea() { printf("*********************************************\n"); printf("** 當前的內存分配情況如下: **\n"); printf("*********************************************\n"); printf("** 起始地址 | 空間大小 | 工作狀態 | 作業號 **\n"); subAreaNode *p = subHead.nxt; while (p != NULL) { printf("**-----------------------------------------**\n"); printf("**"); printf(" %3d k |", p->addr); printf(" %3d k |", p->size); printf(" %s |", p->state == Free ? "Free" : "Busy"); if (p->taskId > 0) { printf(" %2d ", p->taskId); } else { printf(" "); } printf("**\n"); p = p->nxt; } printf("*********************************************\n"); } int main() { int option, ope, taskId, size; // 初始化空閑分區鏈 intSubArea(); // 選擇分配算法 while (1) { printf("\n\n"); printf("\t****************請選擇要模擬的分配算法******************\n"); printf("\n\n"); printf("\t \t 0 首次適應算法 \n"); printf("\n\n"); printf("\t \t 1 最佳適應算法 \n"); printf("\n\n"); printf("\t\t\t\t你的選擇是:"); scanf("%d", &option); if (option == 0) { printf("你選擇了首次適應算法,下面進行算法的模擬\n"); break; } else if (option == 1) { printf("你選擇了最佳適應算法,下面進行算法的模擬\n"); break; } else { printf("錯誤:請輸入 0/1\n\n"); } } // 模擬動態分區分配算法 while (1) { printf("\n"); printf("*********************************************\n"); printf("** 1: 分配內存 2: 回收內存 0: 退出 **\n"); printf("*********************************************\n"); scanf("%d", &ope); if (ope == 0) break; if (ope == 1) { // 模擬分配內存 printf("請輸入作業號: "); scanf("%d", &taskId); printf("請輸入需要分配的內存大小(KB): "); scanf("%d", &size); if (size <= 0) { printf("錯誤:分配內存大小必須為正值\n"); continue; } // 調用分配算法 if (option == 0) { firstFit(taskId, size); } else { bestFit(taskId, size); } // 顯示空閑分區鏈情況 showSubArea(); } else if (ope == 2) { // 模擬回收內存 printf("請輸入要回收的作業號: "); scanf("%d", &taskId); freeSubArea(taskId); // 顯示空閑分區鏈情況 showSubArea(); } else { printf("錯誤:請輸入 0/1/2\n"); } } printf("分配算法模擬結束\n"); system("pause"); return 0; }
五.實驗結果截圖
經過多次內存后:
回收作業1和作業4后:
此時分兩種情況分別模擬首次使用算法和最佳使用算法為作業6分配40KB內存:
模擬首次適應算法:
最佳適用算法:
六.結果分析
通過多個分區分配,當回收作業1釋放100K空間,回收作業4釋放80K空間后,再為作業6分配40K空間。首次適用算法會優先拿作業1釋放的100K空間為作業6分配內存。最佳適用算法則會優先拿作業4釋放的80K空間為作業6分配內存。
根據實驗得出結論,首次適用算法是從空閑分區表的頭指針開始查找,把最先能夠滿足要求的空閑區分配給作業。該算法優先使用低址部分空閑區,在低址空間造成許多小的空閑區,在高地址空間保留大的空閑區,此算法比較節省時間。
最佳適用算法將可利用空閑表中一個不小於“請求”且最接近"請求"的空閑區的一部分分配給用戶。分配與回收都需要對可利用空閑表從頭至尾查詢一遍。在分配時容易產生太小而無法利用的內存碎片,同時這種做法也保留了那些很大的內存塊以備響應將來發生的內存量較大的用戶的請求。這種分配算法適合請求分配內存大小范圍較廣的系統,此算法最費時間。