操作系统课设——设计模拟一个SPOOLING假脱机输出程序
廣州大學操作系統課程設計報告
要求:書寫課程設計報告,報告中應該包含如下內容:
一.課程設計題目及內容
課程設計題目:題目三: 設計模擬一個SPOOLING假脫機輸出程序
(1) 系統設計要求:設計一個SPOOLING輸出進程和兩個請求輸出的用戶進程,以及一個SPOOLING輸出服務程序request。當用戶進程希望輸出一系列信息時,調用SPOOLING輸出服務程序request,由輸出服務程序將該信息送入輸出井。待給出一個結束標志時,表示進程該次的文件輸出結束。之后,申請一個輸出請求塊,用來記錄請求輸出的用戶進程的名字、要輸出的文件名以及要輸出信息的長度等。等待SPOOLING輸出進程進行輸出。這里,SPOOLING輸出進程與請求輸出的用戶進程可并發運行。SPOOLING輸出進程工作時,根據請求塊記錄的各進程要輸出的信息,將其實際輸出到打印機或顯示器,這里記錄到一個文件中。
(2) 進程調度:采用隨機調度算法,這與進程要求輸出信息的隨機性相一致。兩個請求輸出的用戶進程的調度概率各為40%,SPOOLING輸出進程被調度的概率為20%,這由隨機數發生器產生的隨機數來模擬決定。
(3) 進程狀態:進程有三個基本狀態,分別為可執行、等待和結束。狀態變化的條件為:
1) 當進程正在運行或等待調度時的狀態為可執行態。
2) 服務程序在將用戶輸出信息送輸出井時,如發生輸出請求塊已用完,將調用它的用戶進程置為“等待狀態1”。
3) SPOOLING輸出進程在進行輸出時,若發現輸出請求塊為空,則進入“等待狀態2”。
4) SPOOLING輸出進程輸出一個信息塊后,應將正在等待輸出的進程置為“可執行狀態”。
5) 服務程序在輸出信息到輸出井并形成輸出請求信息塊后,若SPOOLING進程處于等待態,則將其置為“可執行狀態”。
6) 進程執行完成時,置為“結束態”。
二.程序中使用的數據結構及主要符號說明
(4) 數據結構:
1) 進程控制塊(PCB)如下:
2) 請求輸出塊reqblock 如下:
struct reqblock //請求輸出塊 {int reqid;//要求輸出的進程 int tname;int length;//輸出長度 int addr;//輸出首地址 }ReqBlock[REQBLOCKNUM];3) 輸出井BUFFER。SPOOLING系統為每個請求輸出的進程在輸出井中分別開辟一個區。本題目可設計一個二維數組(int buffer[T1][30] 和 buffer[T2][30])作為輸出井。每個進程一個文件最多可占用輸出井20個位置。
struct BUFFER //輸出井結構 {int buf[OUTBUFFERNUM]; //輸出井緩沖區int usedNum; //輸出井緩沖區已使用的數目int head; //指示輸出井空閑塊首地址//int tail; //指示輸出井信息塊(有信息的部分)尾地址 }OutBuffer[PROCESSNUM];三.程序流程圖和帶有注釋的源程序
SPOOLING系統輸出模擬的主控流程圖如圖3-1所示:
SPOOLING輸出服務程序request(由請求輸出的兩個用戶進程調用)流程圖如圖3-2所示:
SPOOLING輸出進程流程圖如圖3-3所示:
圖3-1 SPOOLING系統輸出模擬的主控流程圖
圖3-2 SPOOLING輸出服務程序request流程圖
圖3-3 SPOOLING輸出進程流程圖
源程序:
#include "stdio.h" #include "stdlib.h" #include "time.h" #include <iostream> using namespace std;#define PROCESSNUM 2 //輸出進程個數 #define OUTBUFFERNUM 30 //輸出井存儲字節個數 #define REQBLOCKNUM 10 #define T1 3 //定義用戶進程0要輸出的文件數T1 #define T2 3 //定義用戶進程1要輸出的文件數T2struct pcb //進程控制塊PCB {int id; //進程標識 int status; //狀態0為可執行態;等待狀態1,表示請求輸出塊用完,請求輸出的用戶進程等待;等待狀態2,表示輸出井空,SPOOLING輸出進程等待;3為結束態int length;//輸出長度 }PCB[PROCESSNUM + 1];struct reqblock //請求輸出塊 {int reqid;//要求輸出的進程 int tname;int length;//輸出長度 int addr;//輸出首地址 }ReqBlock[REQBLOCKNUM];struct BUFFER //輸出井結構 {int buf[OUTBUFFERNUM]; //輸出井緩沖區int usedNum; //輸出井緩沖區已使用的數目int head; //指示輸出井空閑塊首地址//int tail; //指示輸出井信息塊(有信息的部分)尾地址 }OutBuffer[PROCESSNUM];int C3 = 10; //C3表示當前系統剩余的請求輸出信息塊個數,初值為10 int n_out = 0, n_in = 0; //指示當前使用的輸出請求塊,request從n_in開始取,spooling從n_out開始取int t1 = 0; //設兩個計時器,分別記錄兩個用戶進程已經輸出的文件個數,都初始化為0 int t2 = 0; int t_num[2][10];void init()//初始化函數 {int i, j;for (i = 0; i < PROCESSNUM; i++){OutBuffer[i].head = 0;OutBuffer[i].usedNum = 0;for (j = 0; j < OUTBUFFERNUM; j++)OutBuffer[i].buf[j] = 0;}for (i = 0; i < REQBLOCKNUM; i++){ReqBlock[i].reqid = -1;ReqBlock[i].length = 0;ReqBlock[i].addr = 0;}for (i = 0; i < PROCESSNUM + 1; i++){PCB[i].id = i;PCB[i].status = 0;PCB[i].length = 0;}//自動生成用戶進程0和用戶進程1的文件長度/*for (i = 0; i < T1; i++){t_num[0][i] = (rand() % 30) + 1;}*//*for (i = 0; i < T2; i++){t_num[1][i] = (rand() % 30) + 1;}*///手動生成用戶進程0和用戶進程1的文件長度t_num[0][0] = 15;t_num[0][1] = 18;t_num[0][2] = 8;t_num[1][0] = 25;t_num[1][1] = 18;t_num[1][2] = 6; }void request(int i) {cout << "==============================================================================="<<endl;cout << "進程: " << i << " 調用request進程,寫入的進程塊序號為ReqBlock[" << n_in << "]" << endl;int j, length = 0;if (C3 == 0) //判斷是否有空閑的請求塊{PCB[i].status = 1; //沒有空閑的請求塊,進程狀態置3cout << "沒有空閑的請求塊,進程狀態置1" << endl;return;}C3--;//申請一個空閑的請求輸出塊//判斷輸出文件的字符數是否大于20,如果大于20,標志aa和bb會置為falsebool aa = true;bool bb = true;if (i == 0) //如果是用戶進程0for (int k = 0; k < t_num[0][t1]; k++) {j = (rand() % 10) + 1;//隨機數if (k == 20) {aa = false;cout << endl<<"該文件個數大于20,將被掛起" << endl;t_num[0][t1] = t_num[0][t1] - 20;PCB[0].status = 1;break;}OutBuffer[0].buf[(OutBuffer[i].head + k) % OUTBUFFERNUM] = j; //J送buffer[0][length]cout << j << " ";OutBuffer[0].usedNum++;//判斷輸出井是否滿if (OutBuffer[0].usedNum == OUTBUFFERNUM) {cout << endl<<"輸出井滿" << endl;ReqBlock[n_in].length = k+1;t_num[0][t1] = t_num[0][t1] - ReqBlock[n_in].length;ReqBlock[n_in].reqid = i;ReqBlock[n_in].addr = OutBuffer[i].head;n_in = (n_in + 1) % REQBLOCKNUM; //修改的輸出請求塊的個數加1PCB[0].status = 1; //掛起進程return;}}else { //如果是用戶進程1for (int k = 0; k < t_num[1][t2]; k++) {j = (rand() % 10) + 1;//隨機數if (k == 20) {bb = false;cout << endl<<"該文件個數大于20,將被掛起" << endl;t_num[1][t2] = t_num[1][t2] - 20;PCB[1].status = 1;break;}OutBuffer[1].buf[(OutBuffer[i].head + k) % OUTBUFFERNUM] = j; //J送buffer[0][length]cout << j << " ";OutBuffer[1].usedNum++;//判斷輸出井是否滿if (OutBuffer[1].usedNum == OUTBUFFERNUM) {cout << "輸出井滿" << endl;PCB[1].status = 1; //掛起進程ReqBlock[n_in].length = k+1;t_num[1][t2] = t_num[1][t2] - ReqBlock[n_in].length;ReqBlock[n_in].reqid = i;ReqBlock[n_in].addr = OutBuffer[i].head;n_in = (n_in + 1) % REQBLOCKNUM; //修改的輸出請求塊的個數加1return;}}}cout << endl;if (i == 0) {cout << "進程 " << i << " 文件的" << t1 << "的字符個數:" << t_num[0][t1] << endl << endl;if (aa) {ReqBlock[n_in].length = t_num[0][t1];cout << " <<<<<<<<<<<<<<<<<<<<<<進程結束>>>>>>>>>>>>>>>>>>>>" << endl << endl;t1++; //t1記錄用戶進程1已經輸出的文件個數}elseReqBlock[n_in].length = 20;}else {cout << "進程 " << i << " 文件" << t2 << "的字符個數:" << t_num[1][t2] << endl << endl;if (bb) {ReqBlock[n_in].length = t_num[1][t2];cout << " <<<<<<<<<<<<<<<<<<<<<<進程結束>>>>>>>>>>>>>>>>>>>>" << endl << endl;t2++; //t2記錄用戶進程1已經輸出的文件個數}elseReqBlock[n_in].length = 20;}//填寫請求塊ReqBlock[n_in].reqid = i; //置該輸出請求塊的進程名字為iReqBlock[n_in].addr= OutBuffer[i].head; //修改輸出首地址OutBuffer[i].head = (OutBuffer[i].head + ReqBlock[n_in].length) % OUTBUFFERNUM;if (PCB[PROCESSNUM].status == 2) //若spooling進程阻塞,則修改其狀態為可執行(0)PCB[PROCESSNUM].status = 0;n_in = (n_in + 1) % REQBLOCKNUM; //修改的輸出請求塊的個數加1}void spooling() {//請完成spooling函數的設計if (C3 == 10) {//如果沒有請求塊if (PCB[0].status == 3 && PCB[1].status == 3) {//是否所有輸出進程結束PCB[2].status = 3;return;}else {PCB[2].status = 2;return;}}cout << "*******************************************************************************" << endl;//按照請求塊從輸出井中取數據輸出(打印到屏幕)//遍歷請求塊while (C3 < 10) {int requid = ReqBlock[n_out].reqid;int addr = ReqBlock[n_out].addr;int length = ReqBlock[n_out].length;cout << "addr" << addr << endl;cout << "SPOOLING輸出進程為:" << requid << endl;cout << "調用SPOOLING進程,釋放的進程塊序號為ReqBlock[" << n_out << "]" << endl;cout << "以下為輸出結果:" << endl;int k;if (requid == 0) {for (k = 0; k < length; k++)cout << OutBuffer[0].buf[(addr + k) % OUTBUFFERNUM] << " ";OutBuffer[0].usedNum = OutBuffer[0].usedNum - length;}else {for (k = 0; k < length; k++)cout << OutBuffer[1].buf[(addr + k) % OUTBUFFERNUM] << " ";OutBuffer[1].usedNum = OutBuffer[1].usedNum - length;}cout << endl;C3++;//將數據從輸出井輸出n_out = (n_out + 1) % REQBLOCKNUM;}if (PCB[0].status == 1) //修改阻塞進程狀態為就緒PCB[0].status = 0;if (PCB[1].status == 1)PCB[1].status = 0;cout << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl<<endl;return;}void work()//模擬進程調度 {int i;bool isFinish;srand((unsigned)time(NULL));while (1){i = rand() % 100;if (i <= 40 && PCB[0].status == 0){if (t1 < 3) {request(0);}}else if (i <= 80 && PCB[1].status == 0){if (t2 < 3) {request(1);}}else if (i > 80 && PCB[2].status == 0){spooling();}//所有進程都結束了嗎isFinish = true;if (t1 == T1) {PCB[0].status = 3;}if (t2 == T2) {PCB[1].status = 3;}for (i = 0; i < PROCESSNUM + 1; i++) {if (PCB[i].status != 3)isFinish = false;}if (isFinish) //若所有進程都結束,則退出return;}} int main() //主程序 {srand((unsigned)time(NULL));init();cout << "\n>>>>>>>>>>>>>>>> SPOOLing系統模擬程序 <<<<<<<<<<<<<<<<<\n";cout << "進程0創建" << T1 << "個文件" << endl;for (int i = 0; i < T1; i++) {cout << "進程0文件" << i << "的文件個數是" << t_num[0][i] << endl;}cout << endl;cout << "進程1創建" << T2 << "個文件" << endl;for (int i = 0; i < T2; i++) {cout << "進程1文件" << i << "的文件個數是" << t_num[1][i] << endl;}cout << endl;work();return 0;}四.執行程序名,并打印程序運行時的初值和運算結果
五.實驗結果分析,實驗收獲和體會
操作系統是比較復雜,抽象的課程。單單看書,很難領會操作系統的知識。只有多動手,多做實驗,才會對操作系統的知識有更深刻的體會,比如文件管理,SPOOLING技術等。紙上得來終覺淺,絕知此事要躬行。
六.實驗的改進意見和建議。
1.我覺得操作系統課程設計放在期末考試前是一種不妥的行為,一方面學生要忙于復習,另一方面又要忙于課程設計。既影響復習也影響課設完成的質量。
總結
以上是生活随笔為你收集整理的操作系统课设——设计模拟一个SPOOLING假脱机输出程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 天天生鲜项目页面——商品列表页
- 下一篇: jenkins详细入门教程