操作系统课程设计(作业调度、内存管理、进程调度、进程阻塞等)
操作系統課程設計
資源下載:https://download.csdn.net/download/fufuyfu/85811450
一、課程設計目的
操作系統是計算機系統配置的基本軟件之一。它在整個計算機系統軟件中占有中心地位。其作用是對計算機系統進行統一的調度和管理,提供各種強有力的系統服務,為用戶創造既靈活又方便的使用環境。本課程是計算機及應用專業的一門專業主干課和必修課。
通過課程設計,使學生掌握操作系統的基本概念、設計原理及實施技術,具有分析操作系統和設計、實現、開發實際操作系統的能力。
二、課程設計內容和要求
1、提交一批作業(>=10),按先來先服務選擇一部分作業(最多5個)進入內存
2、為每個作業創建一個進程,并分配內存(用戶內存:0—1024K,采用可變連續分配方式)
3、進程調度功能(時間片輪轉)
4、隨機阻塞進程,并在一段時間后喚醒進程(選做)
5、顯示相關信息:后備作業隊列、內存分配情況、進程信息、完成作業情況
6、這些功能要有機地連接起來
三、軟、硬件環境
軟件:Window10,Dev-C++
硬件:CPU:Intel? Core? i5-8265U CPU @ 1.60GHz 1.80 GHz
RAM:8.00GB
四、設計步驟及實現
1、課程設計題目分析
本次課程設計將作業調度(先來先服務算法)、動態內存管理(首次適應算法)、進程調度(時間片輪轉調度算法)、進程阻塞(隨機阻塞進程和喚醒進程)等功能有機結合。首先,需要按照用戶輸入的作業數隨機初始化相應的作業并構成后備作業隊列,同時初始化內存分區表以及進程隊列,即后備作業隊列模擬外存,進程隊列模擬內存,內存分區表用于內存管理,然后使用先來先服務(FCFS)進行作業調度,即模擬將作業從外存調入到內存,再使用時間片輪轉調度算法(RR)進行進程調度。其中,最多只能有5個作業同時進入內存,即內存中處于運行、等待、阻塞的進程數之和最多為5個。因此在本次課程設計中我用一個全局變量記錄當前進程數,當前進程數小于5時,從后備作業隊列中調入已到達并且未完成作業并為該作業創建進程同時使用首次適應算法(FF)為其分配內存,再進行進程調度,并在進程調度的過程中,隨機阻塞進程,并在一定的時間后喚醒進程。本次課程設計中我用一個指針記錄當前執行的進程,每一次從該進程開始找進程為wait并執行一定時間。若當前進程完成(即包含作業已完成)時,釋放該進程占用的內存并將該已完成的作業調到后備作業隊列的末尾,即已完成作業從內存調出。整個進程調度的過程直到后備作業隊列的第一個作業為完成狀態以及進程隊列為空則結束。
2、算法流程圖
3、程序代碼
3.1 頭文件及數據結構
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h>#define TIME_SLICE 2 #define Max_Memory 1024 #define Block_Time 8// 進程、作業狀態:就緒 等待 阻塞 完成 enum status{status_wait = 'W',status_run = 'R',status_block = 'B',status_finish = 'F' };// 進程控制塊結構體 typedef struct PCB{ // 進程名char process_name[5]; // 到達時間int arrive_time;// 仍需時間int need_time;// 進程狀態:運行、就緒、阻塞、完成// 但完成狀態不顯示在進程隊列中,完成則調出內存 char status;// 進程阻塞時長int block_time; // 占用內存起址 int begin;// 占用內存大小 int size;// 占用作業 char task_name[5];// 后向指針PCB *next; }PCB; // 內存塊結構體 typedef struct Node{ // 分區起址 int begin; // 分區大小 int size; // 分區狀態:Busy(1)或Free(0) int status; // 占用進程 char process_name[5]; // 后向指針Node *next; }Node;// 作業結構體 typedef struct Task{// 作業名char task_name[5]; // 到達時間int arrive_time;// 所需時間int need_time;// 所需內存 int size;// 作業狀態:等待、完成 char status; // 后向指針Task *next; }Task;// 進程調度總時間 int Total_time = 0; // 記錄當前執行進程的位置 PCB *runIndex = NULL; // 記錄當前進程隊列個數 int sum = 0; // 記錄已經生成的進程數 int number = 0;3.2 函數聲明及主函數
// 函數聲明 // 初始化內存分區 void initNode(Node *node); // 首次適應算法分配內存 bool first_fit(Node *node_list,PCB *new_process); // 隨機產生一批作業(>=10個) void create_task(Task *task_list); // 按到達時間插入新作業 void insert_task(Task *task_list,Task *new_task); // 將已完成作業調整到作業隊列尾部 void finish_task(Task *task_list,PCB *finish_process); // 先來先服務選擇作業 void change_task(Node *node_list,Task *task_list,PCB *process_list); // 創建新進程 bool create_process(PCB *process_list,Task *move_task,Node *node_list); // 按起始地址插入新進程 void insert_process(PCB *process_list,PCB *new_process); // 隨機阻塞進程 void block_process(PCB *process_list); // 喚醒進程 void notify_process(PCB *process_list); // 時間片輪轉調度算法 void round_robin(Node *node_list,Task *tasl_list,PCB *process_list); // 展示:內存分區表、內存分配情況 void show_node(Node *node_list); // 展示:后備作業隊列、作業完成情況 void show_task(Task *task_list); // 展示:進程運行情況、作業完成情況 void show_process(PCB *process_list);// 主函數 int main(){srand(time(NULL));// 創建內存分區鏈表Node *node_list = (Node*)malloc(sizeof(Node));if(node_list == NULL){printf("動態內存分配失敗!"); }else{node_list->next = NULL;}initNode(node_list); // 創建后備作業隊列 Task *task_list = (Task*)malloc(sizeof(Task));if(task_list == NULL){printf("動態內存分配失敗!"); }else{task_list->next = NULL;}create_task(task_list);// 創建空進程隊列PCB *process_list = (PCB*)malloc(sizeof(PCB));if(process_list == NULL){printf("動態內存分配失敗!"); }else{process_list->next = NULL;}printf("初始狀態:\n"); show_node(node_list);show_task(task_list);show_process(process_list);// 調用時間片輪轉調度算法 round_robin(node_list,task_list,process_list); }3.3 初始化內存分區
// 初始化內存分區 void initNode(Node *node){Node *new_node = (Node*)malloc(sizeof(Node));if(new_node == NULL){printf("動態內存分配失敗!"); }// 分區起址new_node->begin = 0;// 分區大小 new_node->size = Max_Memory;// 分區狀態:0代表空閑,1代表占用 new_node->status = 0;// 進程名sprintf(new_node->process_name,"%s","無"); // 后向指針 new_node->next = NULL;// 將該初始化分區放入分區鏈表 node->next = new_node; }3.4 首次適應算法分配內存
// 首次適應算法分配內存 bool first_fit(Node *node_list,PCB *new_process){ Node *move = node_list->next;// 空閑內存分區鏈不為空 while(move != NULL){// 空閑的空間if(move->status == 0){ // 剩余空間大于作業需要的內存空間,可分配 if(move->size > new_process->size){ // 分配后剩余的空間 Node *p = (Node*)malloc(sizeof(Node));p->begin = move->begin + new_process->size;p->size = move->size - new_process->size;p->status = 0;sprintf(p->process_name,"%s","無"); // 分配給進程的空間move->size = new_process->size;move->status = 1;sprintf(move->process_name,"%s",new_process->process_name);// 改變節點的連接 p->next = move->next; move->next = p;break; }else if(move->size == new_process->size){ // 空閑空間和作業需要的內存空間大小相等時,可分配 move->status = 1;sprintf(move->process_name,"%s",new_process->process_name);break;} }// 已到空閑內存分區鏈末尾 if(move->next == NULL){printf("內存分配失敗,沒有足夠大的內存分配給該進程!\n");return false;}move = move->next;}new_process->begin = move->begin;show_node(node_list);return true; }3.5 回收內存并將其中作業調整到后備作業隊列
// 回收內存并將其中作業調整到后備作業隊列 void recycle(Node *node_list,PCB *finish_process){Node *move = node_list->next; while(true){// 當進程隊列的第一個進程占用內存需釋放時 if(strcmp(move->process_name,finish_process->process_name) == 0){ move->status = 0; sprintf(move->process_name,"%s","無");break;}else if(move->status == 0 && strcmp(move->next->process_name,finish_process->process_name) == 0){// 當move指向需釋放空間的前驅結點,需釋放空間的上一塊空間空閑時// 合并需釋放空間上一塊空間和需釋放空間 move->size = move->size + move->next->size;Node *q = move->next;move->next = move->next->next;// 釋放內存 free(q);break;}else if(strcmp(move->next->process_name,finish_process->process_name) == 0){ // 需釋放空間的上一塊空間忙碌時 // move指向當前釋放的內存空間 move = move->next; move->status = 0; sprintf(move->process_name,"%s","無"); break;}else if(move->next == NULL){ // 已走到鏈表末尾,此時表明進程名都不匹配 printf("此進程不存在!\n");break;}move = move->next;}// 需釋放空間的下一個空間空閑時 if(move->next != NULL && move->next->status == 0){ move->size = move->size + move->next->size;Node *q = move->next;move->next = move->next->next;free(q);} }3.6 隨機產生一批作業(>=10)
// 隨機產生一批作業(>=10) void create_task(Task *task_list){printf("\n請輸入產生的作業數(>=10):");int number; scanf("%d",&number);printf("\n");int i = 1;// 循環創建后備作業隊列 while(i <= number){// 創建新作業 Task *new_task = (Task*)malloc(sizeof(Task)); if(new_task == NULL){printf("動態內存分配失敗!"); }// 作業名sprintf(new_task->task_name,"%s%d","T",i);// 到達時間(0~8)if(task_list->next == NULL){new_task->arrive_time = 0;}else{new_task->arrive_time = rand()%19;}// 服務時間(1~10) new_task->need_time = rand()%12+1;// 所需內存(31~200)new_task->size = rand()%(Max_Memory/5)+31;// 作業狀態new_task->status = status_wait; // 后向指針new_task->next = NULL; // 創建下一作業++i;// 將新作業插入后備作業隊列insert_task(task_list,new_task); } }3.7 按到達時間插入新作業
// 按到達時間插入新作業 void insert_task(Task *task_list,Task *new_task){int arrive_time = new_task->arrive_time;Task *move = task_list;while(move->next != NULL){if(move->next->arrive_time > arrive_time){new_task->next = move->next;move->next = new_task;return;}move = move->next;}move->next = new_task; }3.8 將已完成作業調整到作業隊列尾部
// 將已完成作業調整到作業隊列尾部 void finish_task(Task *task_list,PCB *finish_process){Task *finish_task = (Task*)malloc(sizeof(Task));if(finish_task == NULL){printf("動態內存分配失敗!"); }sprintf(finish_task->task_name,"%s",finish_process->task_name);finish_task->arrive_time = finish_process->arrive_time;finish_task->need_time = finish_process->need_time;finish_task->size = finish_process->size;finish_task->status = finish_process->status;finish_task->next = NULL; Task *move = task_list;while(move->next != NULL){move = move->next;}move->next = finish_task;show_task(task_list); }3.9 先來先服務選擇作業
// 先來先服務選擇作業 void change_task(Node *node_list,Task *task_list,PCB *process_list){/*由于進程最多5個并發,即等待、阻塞、運行的進程數最多為5個所以從作業隊列中選擇作業不能使進程超過5個,而且要考慮作業是否到達 */// 選擇作業Task *move = task_list->next;while(sum < 5 && move != NULL && move->status != status_finish && move->arrive_time <= Total_time){// 先保存要調入內存的作業 Task *move_task = move;// 為該作業創建進程if(create_process(process_list,move_task,node_list)){// 分配內存成功 sum++;// 剔除作業Task *p = task_list;while(p->next != move){p = p->next;} p->next = move->next;move = p->next;}else{move = move->next;} } }3.10 為新調入內存的作業創建新進程
// 為新調入內存的作業創建新進程 bool create_process(PCB *process_list,Task *move_task,Node *node_list){PCB *new_process = (PCB*)malloc(sizeof(PCB));if(new_process == NULL){printf("動態內存分配失敗!"); }// 進程名 sprintf(new_process->process_name,"%s%d","P",++number);// 到達時間new_process->arrive_time = move_task->arrive_time; // 仍需時間new_process->need_time = move_task->need_time;// 進程狀態new_process->status = move_task->status;// 阻塞時長new_process->block_time = 0; // 占用內存起址 new_process->begin = 0;// 占用內存大小 new_process->size = move_task->size;// 占用作業 sprintf(new_process->task_name,"%s",move_task->task_name);// 后向指針new_process->next = NULL; // 為新進程分配內存 if(!first_fit(node_list,new_process)){free(new_process); return false;}// 將新進程放入進程隊列insert_process(process_list,new_process);return true; }3.11 按起始地址插入新進程
// 按起始地址插入新進程 void insert_process(PCB *process_list,PCB *new_process){int begin = new_process->begin;PCB *move = process_list;while(move->next != NULL){if(move->next->begin > begin){new_process->next = move->next;move->next = new_process;return;}move = move->next;}move->next = new_process;show_process(process_list); }3.12 隨機阻塞進程
// 隨機阻塞進程 void block_process(PCB *process_list){// 隨機生成阻塞進程名 int block_id = rand()%number+1;char block_pro[5];sprintf(block_pro,"%s%d","P",block_id);PCB *move = process_list->next;while(move != NULL){if(strcmp(move->process_name,block_pro) == 0){// 更新進程狀態 move->status = status_block;// 將阻塞時長置0 move->block_time = 0;printf("阻塞進程:%s\n",move->process_name);return;}move = move->next;} }3.13 喚醒進程
// 喚醒進程 void notify_process(PCB *process_list){PCB *move = process_list->next;while(move != NULL){if(move->block_time >= Block_Time){// 更新進程狀態 move->status = status_wait;// 將阻塞時長置0 move->block_time = 0;printf("喚醒進程:%s\n",move->process_name);}move = move->next;} }3.14 時間片輪轉調度算法
// 時間片輪轉調度算法 void round_robin(Node *node_list,Task *task_list,PCB *process_list){/*用一個全局變量記錄當前進程數,當前進程數小于5時,從作業隊列中調入已到達作業并為作業創建進程分配內存用一個指針記錄當前執行的進程,每一次從該進程開始找進程為wait并執行一個時間片當一個進程完成時,釋放該進程占用的內存并將該已完成的作業調到后備作業隊列的末尾 整個調度過程直到后備作業隊列的第一個作業為完成狀態以及進程隊列為空則結束在進程調度的過程中,隨機阻塞進程:一定的時間后喚醒進程 */// 記錄當前執行進程的位置,初始指向進程隊列的第一個進程 runIndex = process_list->next; while (sum != 0 || task_list->next->status != status_finish) {// 保存舊進程數 int old_sum = sum;// 當前進程數小于5并且仍然有作業未完成,從后備隊列選擇作業if(sum < 5 && task_list->next != NULL && task_list->next->status != status_finish){change_task(node_list,task_list,process_list); }// 隨機阻塞進程 block_process(process_list);// 喚醒進程notify_process(process_list); // 剛好runIndex指向NULL且當前進程數增加 if(runIndex == NULL && old_sum < sum){// 找出之前進程的最后一個 int move_sum = 0;PCB *move = process_list; while(move_sum < old_sum){move_sum++;move = move->next;} // runIndex指向新加入的進程 runIndex = move->next;}else if(runIndex == NULL){// 當runIndex指向NULL且當前進程數未增加// runIndex重新指向進程隊列的第一個進程 runIndex = process_list->next;} // 經過以上處理runIndex仍然為NULL,則當前無進程無作業到達 if(runIndex == NULL){printf("當前無進程且無作業到達!\n");printf("等待作業到達。。。。。。\n");printf("等待時長:%d\n",task_list->next->arrive_time - Total_time);Total_time += task_list->next->arrive_time - Total_time;printf("進程調度總時間:%d\n",Total_time);continue; }// 循環遍歷進程隊列,找到進程狀態為wait的進程并執行PCB *memoryIndex = runIndex;while(runIndex->status != status_wait){runIndex = runIndex->next; // runIndex走到進程隊列末尾 if(runIndex == NULL){runIndex = process_list->next;}// runIndex循環一遍后仍找不到狀態為wait的進程 if(runIndex == memoryIndex){show_process(process_list);// 表示所有進程都阻塞// 找出距離阻塞進程被喚醒最近的進程,等待該進程被喚醒int notify_time = Block_Time - process_list->next->block_time;PCB *move = process_list->next;while(move != NULL){// 如果有距離阻塞進程被喚醒更近的進程 if(Block_Time - move->block_time < notify_time){// 更新喚醒時長notify_time = Block_Time - move->block_time; }move = move->next;} printf("當前進程全部阻塞,等待進程被喚醒!\n");printf("等待時長:%d\n",notify_time);// 此時已找出距離阻塞進程被喚醒最近時長move = process_list->next;while(move != NULL){// 等待進程被喚醒 // 更新所有進程的阻塞時長move->block_time += notify_time; move = move->next;}// 更新進程調度總時間 Total_time += notify_time; show_process(process_list); // 喚醒進程 notify_process(process_list);show_process(process_list); // 重新進行進程調度 printf("進程調度總時間:%d\n",Total_time);continue;}}// 執行當前進程 runIndex->status = status_run; show_process(process_list);// 判斷該進程是否能完成 if (runIndex->need_time-TIME_SLICE > 0) {// 進程執行一個時間片后無法完成 // 更新進程調度總時間 Total_time += TIME_SLICE;// 更新進程仍需時間 runIndex->need_time -= TIME_SLICE;// 更改進程狀態runIndex->status = status_wait; // 指向下一進程 runIndex = runIndex->next; // 更新阻塞時長PCB *p = process_list->next;while(p != NULL){if(p->status == status_block){p->block_time += TIME_SLICE;}p = p->next;} }else{// 進程可以完成// 更新阻塞時長PCB *p = process_list->next;while(p != NULL){if(p->status == status_block){p->block_time += runIndex->need_time;}p = p->next;}// 更新進程調度總時間 Total_time += runIndex->need_time;// 更新進程仍需時間 runIndex->need_time = 0;runIndex->status = status_finish;show_process(process_list);// 移除該進程同時釋放內存并將其中作業調整到后備作業隊列PCB *finish_process = runIndex;PCB *move = process_list;while(move->next != runIndex){move = move->next;}// 移除進程 move->next = runIndex->next;// 指向下一進程 runIndex = move->next; // 將其中作業調整到后備作業隊列并釋放內存finish_task(task_list,finish_process); recycle(node_list,finish_process); free(finish_process);sum--;} show_node(node_list);show_task(task_list);show_process(process_list);printf("進程調度總時間:%d\n",Total_time);} }3.15 展示函數
// 展示:內存分區表、內存分配情況 void show_node(Node *node_list){Node *move = node_list->next;printf("************************************************************************************\n");printf(" 內存分區表 \n");printf("---------------------\n");printf("|起始地址 分區大小 占用進程 分區狀態|\n");while(move != NULL){if(move->status == 0){printf("| %d\t %d\t %s\t Free |\n",move->begin,move->size,move->process_name,move->status);}else{printf("| %d\t %d\t %s\t Busy |\n",move->begin,move->size,move->process_name,move->status);}move = move->next;}printf("---------------------\n"); } // 展示:后備作業隊列、作業完成情況 void show_task(Task *task_list){Task *move = task_list->next;printf(" 后備作業隊列 \n");printf("~~~~~~~~~~~~~~~~~~~~~~~~~~\n");printf("|作業名 到達時間 所需時間 所需內存 作業狀態|\n");while(move != NULL){printf("| %s\t %d\t %d\t %d\t %c |\n",move->task_name,move->arrive_time,move->need_time,move->size,move->status);move = move->next;}printf("~~~~~~~~~~~~~~~~~~~~~~~~~~\n"); }// 展示:進程運行情況、作業完成情況 void show_process(PCB *process_list){PCB *move = process_list->next;printf(" 當前各進程PCB信息 \n");printf("﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀\n");printf("|進程名 到達時間 仍需時間 進程狀態 阻塞時長 內存起址 內存大小 占用作業|\n");while(move != NULL){printf("| %s\t %d\t %d\t %c\t %d\t %d\t %d\t %s |\n",move->process_name,move->arrive_time,move->need_time,move->status,move->block_time,move->begin,move->size,move->task_name);move = move->next;}printf("﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀﹀\n"); }4、運行結果及分析(由于運行結果過多,此處僅展示關鍵部分)
用戶向系統提交需隨機產生10個作業,此時為進程調度的初始狀態。內存尚為進行分配,暫無進程信息,內存初始化大小為1024K,隨機生成的10個作業已按到達時間排序成后備作業序列(外存)。
此時已從外存中將作業T1和T9調入內存,分配所需內存并創建進程P1和P2,同時此時隨機阻塞了進程P2,P1進程運行一個時間片(2),同時P2進程更新阻塞時長,當P2進程阻塞時長達到一定值時,會被喚醒。
此時內存中已有P1~P5共5個進程,后備作業隊列中仍未有作業完成,當前進程調度總時間為20,此時阻塞進程P5,即進程P5的阻塞時長置0,喚醒進程P1,即進程P1的阻塞時長置0的同時更改進程狀態為等待(W),又剛好排到進程P1執行,更改進程狀態為運行(R),并在此過程中,該進程所包含的作業T1可完成,所以完成作業T1后,將該作業調整到后備作業隊列的末尾并將其對應進程P1所占用的內存釋放。
此時當前進程僅剩進程P6,當進程P6被阻塞時,會就此等待進程P6的阻塞時長到達特定值后喚醒并再次進行進程調度,雖然進程P6可能再次被阻塞,但最終還是會完成進程P6。此邊界情況如進程隊列中所有進程同時阻塞,此時會等待進程中距離喚醒時長最短的進程被喚醒并再次進行進程調度。雖然進程可能再次被阻塞,但最終還是會完成該進程??煞乐钩霈F因所有進程均阻塞而進入死循環,使程序更具有穩定性。
此時最后一個進程P6中所包含的作業T7完成,進程調度總時間為90,此時進程隊列已為空,內存分區表回到初始狀態,后備作業隊列中所有的作業狀態均顯示已完成,表示此次整個進程調度的過程已結束。
五、心得體會
我在完成此次課程設計之前已自主完成了進程調度、銀行家算法、內存管理、磁盤調度四個實驗,因此在完成課程設計的過程中沒有出現什么原則上的錯誤,但還是會出現一些小紕漏,如在創建進程時內存申請不成功便將作業從后備作業隊列中剔除,導致程序運行結果混亂。因此我在剔除作業前先嘗試為進程申請內存,若申請成功,則返回true告知可以從后備作業隊列中剔除作業,這樣就不會出現差錯了。在本次課程設計中,與以往進程調度實驗不同的是要求隨機阻塞進程,我使用隨機生成進程號,并判斷當前進程隊列中是否存在匹配進程,有則阻塞,無則放棄。同時在進程結構體中設置阻塞時長字段,在進程調度過程中,更新被阻塞進程的阻塞時長,當該阻塞時長達到特定值時,對相應進程進行喚醒,較好的解決的阻塞與喚醒進程的要求。
本次課程設計的難點在于如何將作業調度、進程調度、內存分配管理等有機地結合來。雖然該過程難度較高,但通過親手編寫代碼,一步一步地分析與調試,最后完成需求功能,滿足了內心大大的成就感,激起我對操作系統學習的興趣,同時也在一定程度加深了我對操作系統的部分內部運行原理的理解,更進一步掌握了操作系統的設計、分析、實現的流程??傊?#xff0c;收獲良多。
總結
以上是生活随笔為你收集整理的操作系统课程设计(作业调度、内存管理、进程调度、进程阻塞等)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 前端学习(1742):前端调试值之快速调
- 下一篇: 超级终端