基于C# winform的操作系统课程设计:SPOOLING假脱机输入输出技术模拟
目錄
- 一、需求分析
- 1.題目要求
- 2.用戶進程分析
- 3.SPOOLING輸出進程分析
- 4.并發分析
- 二、整體功能及設計
- 1.功能劃分
- 2.初始化函數
- 3.調度函數
- 4.用戶進程函數
- 5.SPOOLING輸出函數
- 三、編程實現
- 1.數據結構
- 1.1 用戶進程PCB
- 1.2 輸出請求塊OutPutReqBlock
- 1.3 輸出井OutputWell
- 2.初始化函數
- 3.調度函數
- 4.用戶進程函數
- 5.SPOOLING輸出函數
- 6.主函數
- 7.參數說明
- 四、使用說明
- 1.界面
- 2.初始化
- 3.結果展示
- 五、結果分析
- 六、源碼
一、需求分析
1.題目要求
要求設計一個SPOOLING輸出進程和兩個請求輸出的用戶進程,以及一個SPOOLING輸出服務程序。當請求輸出的用戶進程希望輸出一系列信息時,調用輸出服務程序,由輸出服務程序將該信息送入輸出井。待遇到一個輸出結束標志時,表示進程該次的輸出文件輸出結束。之后,申請一個輸出請求塊(用來記錄請求輸出的用戶進程的名字、信息在輸出井中的位置、要輸出信息的長度等),等待SPOOLING進程進行輸出。
SPOOLING輸出進程工作時,根據請求塊記錄的各進程要輸出的信息,將其實際輸出到打印機或顯示器。這里,SP00LING輸出進程與請求輸出的用戶進程可并發運行。
進程調度采用隨機算法,這與進程輸出信息的隨機性相一致。兩個請求輸出的用戶進程的調度概率各為45%,SPOOLING輸出進程為10%,這由隨機數發生器產生的隨機數來模擬決定。
進程基本狀態有3種,分別為可執行、等待和結束。可執行態就是進程正在運行或等待調度的狀態;等待狀態又分為等待狀態1、等待狀態2和等待狀態3。
狀態變化的條件為:
①進程執行完成時,置為“結束”態。
②服務程序在將輸出信息送輸出井時,如發現輸出井已滿,將調用進程置為“等待狀態1”。
③SPOOLING進程在進行輸出時,若輸出井空,則進入“等待狀態2”。
④SPOOLING進程輸出一個信息塊后,應立即釋放該信息塊所占的輸出井空間,并將正在等待輸出的進程置為“可執行狀態”。
⑤服務程序在輸出信息到輸出井并形成輸出請求信息塊后,若SPOOLING進程處于等待態,則將其置為“可執行狀態”。
⑥當用戶進程申請請求輸出塊時,若沒有可用請求塊時,調用進程進入“等待狀態3”。
2.用戶進程分析
系統中一共有兩個請求輸出的用戶進程,兩個進程分別命名為用戶進程A和用戶進程B。用戶需要輸出的文件可能不止一個,文件之間由一個輸出結束標志隔開,本文實驗采用的文件輸出結束標志為#號。
用戶需要在初始化階段輸入要進行輸出的全部文件內容,然后存進一個數組中。在用戶進程被調度時,如果滿足以下三個條件:當前還有文件沒有被輸出、輸出井中剩余空間能夠放下該文件、有可用的輸出請求塊,就將該文件送到輸出井,然后申請一個輸出請求塊,加入請求塊等待隊列,等待SPOOLING輸出進程進行調度輸出。
3.SPOOLING輸出進程分析
輪到SPOOLING輸出進程占用CPU時,SPOOLING輸出進程首先檢查請求塊等待隊列中是否有輸出塊需要輸出,沒有就進入等待狀態。否則,進行輸出,然后釋放輸出井空間以及相應的請求輸出塊,并喚醒因為沒有可用輸出塊而陷入沉睡的用戶進程。
4.并發分析
SPOOLING輸出進程與用戶進程可以并發執行。本文將進程的一次執行過程(執行后進程不一定結束)抽象成一個函數,每次先產生一個隨機數,根據隨機數執行某一進程,如果該進程因為某些情況被阻塞,則產生下一個隨機數,調度另外的進程。用戶進程和SPOOLING輸出進程因為各種原因被輪流調度,即為并發執行。
二、整體功能及設計
1.功能劃分
整個系統的功能分為以下幾個部分:初始化函數、調度函數、用戶進程函數、SPOOLING輸出函數。初始化函數用于實現用戶文件的初始輸入與保存;調度函數實現用戶進程和SPOOLING輸出進程間的切換;用戶進程函數實現進程被調度后所完成的一系列動作;SPOOLING輸出函數表示輸出操作。
系統運行總的流程圖如圖1所示:
系統首先利用初始化函數對用戶輸入的內容進行初始化,然后產生一個0到1之間的隨機數R,根據R的大小判斷當前應該執行用戶進程還是輸出進程。等用戶進程和輸出進程都執行完畢后,程序運行結束,否則繼續進行調度。
2.初始化函數
用戶需要輸入自己想要“打印”的內容,初始化函數接受用戶輸入的內容后按照文件結束符進行切割,并將切割好的文件放入一個數組中,等用戶進程被調度時再將其送往輸出井中。
3.調度函數
進程調度采用隨機算法,兩個請求輸出的用戶進程的調度概率各為45%,SPOOLING輸出進程為10%,本文采用隨機數來實現這一要求。進行進程調度時,隨機生成一個0到1之間的小數。如果該數小于等于0.45就將用戶進程A投入運行;如果該數處于0.45到0.9之間,就將用戶進程B投入運行;如果該數大于0.9,就將SPOOLING輸出進程投入運行。
4.用戶進程函數
用戶進程函數首先需要檢查當前進程是否滿足三個條件:還有文件沒有被輸出、輸出井中剩余空間能夠放下該文件、有可用的輸出請求塊,滿足三個條件就將該文件送到輸出井并申請相應請求塊。
用戶進程函數執行的流程圖如圖2所示:
用戶進程執行時,如果發現文件已經輸出完畢,則進程運行結束。否則判斷輸出井是否有剩余空間,無進入等待狀態1。輸出井有剩余空間繼續判斷是否有可用輸出塊,有就將文件送往輸出井并請求一個輸出塊,并喚醒可能沉睡的輸出進程,否則進入等待狀態3。
5.SPOOLING輸出函數
SPOOLING輸出函數檢查是否有可輸出的請求塊,有則進行輸出并釋放相關資源,否則SPOOLING輸出進程等待。
SPOOLING輸出函數的流程圖如圖3所示:
三、編程實現
1.數據結構
1.1 用戶進程PCB
PCB的定義如下:
class PCB {/** 進程描述*/public int id; //序號public int status; //狀態,0表示可執行,123表示三個等待狀態,4表示結束public string[] contents = new string[MaxFileCount]; //要輸出的內容public int[] flags = new int[MaxFileCount]; //為1表示該文件已經被輸出,初始全部為0public int fileCount; //用戶真實輸入的文件個數 }用戶進程中包括序號id、進程狀態status、要輸出的內容contents、文件輸出標志flags以及真實文件個數fileCount。
其中,用戶進程可能存在的進程狀態有:0表示可執行狀態、1表示等待狀態1、3表示等待狀態3、4表示進程結束。
1.2 輸出請求塊OutPutReqBlock
OutPutReqBlock定義如下:
class OutputReqBlock {/** 輸出請求塊*/public int id; //要求進行輸出的進程的idpublic int start; //文件在輸出井中的起始位置public int length; //文件長度public int fileIndex; //要輸出文件的序號public OutputReqBlock(int id, int start, int length, int fileIndex) {this.id = id;this.start = start;this.length = length;this.fileIndex = fileIndex;} }請求輸出塊中包括:請求該請求塊的進程id,文件在輸出井中的起始位置start、文件長度length、要輸出的文件在用戶所有文件中的序號。
1.3 輸出井OutputWell
OutputWell的定義如下:
class OutputWell {/** 輸出井*/public char[] buffer = new char[MaxWellLen]; //輸出緩沖區public int begin = 0; //當前可用位置public int restSize = MaxWellLen; //剩余容量 }輸出井的參數有:緩沖區buffer,用于存放用戶放入的數據;當前可用位置begin,文件在輸出井中按順序存放,begin始終指向當前可用緩沖區的起始位置;剩余容量restSize,緩沖區中剩余的容量,初始時為緩沖區長度MaxWellLen。
2.初始化函數
用戶在文本框中輸入要“打印”的信息,然后選擇輸出內容屬于哪一個進程(A or B),最后點擊初始化按鈕,即可啟動初始化函數。初始化函數首先利用一個string對象存儲用戶輸入的內容。隨后,檢查用戶輸入的內容是否以#號結尾,不合法則提示用戶重新輸入。輸入合法后,將用戶輸入的內容按#號進行切割,切割形成多個字符串,最后利用生成的各項信息初始化一個PCB對象,放入等待隊列waitQueue中。
由于用戶可能多次點擊初始化按鈕,所以每次點擊前需要判斷當前進程是否已經初始化完成,如果已經初始化完成用戶卻再次點擊初始化按鈕,則會覆蓋原來的內容。
輸出井在系統界面進行加載時自動初始化。
初始化函數代碼略!
3.調度函數
為了實現隨機性,每次要進行調度時,就利用C#的Random函數生成一個0到1之間的隨機數。如果該隨機數小于等于0.45表示接下來要調度用戶進程A;如果該隨機數處于0.45到0.9之間表示接下來要調度用戶進程B;如果該隨機數大于0.9表示接下來要調度SPOOLING輸出進程。
調度函數的實現如下:
private int dispatch() {/** 進程調度*/double res = rd.NextDouble(); //產生一個01之間的小數if(res <= 0.45) {return 0;}else if(res <= 0.9) {return 1;}else {return 2; //012分別表示兩個進程和SPOOLing輸出進程} }4.用戶進程函數
用于實現用戶進程運行時所進行的一系列操作。
用戶進程被調度時,首先檢查是否還有文件未送到輸出井,沒有則置當前用戶進程為結束狀態,函數返回。
用戶進程尚未結束,說明還有文件未被送入輸出井。循環查找一個尚未輸出的文件塊(相應flag標志為1),接著查詢輸出井中的剩余空間是否還能放下此文件塊,如果不能,將進程狀態置為等待狀態1,函數返回。若還有剩余空間,接著檢查是否還有可用的請求輸出塊,如果沒有將進程置為等待狀態3,函數返回。否則將文件塊送入輸出井并修改輸出井相關參數,然后申請一個請求輸出塊,放入輸出隊列printQueue中,等待SPOOLING輸出進程被調度時進行打印輸出。最后,如果SPOOLING輸出進程處于等待狀態,該用戶進程需要將其喚醒。
用戶進程函數運行時的各種情況通過一個列表進行保存,用于最后的結果展示。列表內容包括:當前調度序號、進程號、進程狀態、輸出井狀態、可用請求塊個數、文件序號、文件長度。
用戶進程函數代碼略!
5.SPOOLING輸出函數
輸出函數的功能是選擇一個請求輸出塊,然后對其中的內容進行輸出,最后釋放掉相應資源。
首先檢查輸出井是否為空,空就置輸出進程為等待狀態2,函數返回。否則檢查請求輸出隊列中是否有需要輸出的請求輸出塊,沒有函數返回。否則從請求輸出隊列中取出隊首的請求輸出塊,然后輸出請求塊,并釋放相應的輸出井空間和請求塊。
輸出函數進行輸出時,要將輸出內容顯示到文件輸出區域。
輸出函數代碼略!
6.主函數
用戶點擊“程序運行”按鈕后開始運行主函數,主函數中根據當前情況來動態調整運行的進程。
主函數首先判斷兩個用戶進程是否都已經初始化完畢,初始化完畢才能運行,否則提示出錯。
初始化完畢后再次點擊“程序運行”按鈕,只要有一個進程未處于結束狀態,或者有一個請求塊尚未被輸出,則繼續調度。調度時要判斷當前進程是否已經結束,結束就輸出相關狀態。
主函數代碼略!
7.參數說明
實驗中用到的各種參數說明如表1所示:
| 參數說明 | 輸出井長度 | 一個用戶可以輸出的最大文件數 | 請求塊個數 |
| 參數值 | 15 | 10 | 3 |
四、使用說明
1.界面
系統界面如圖4所示:
系統界面分為三個版塊:初始化、調度過程以及文件輸出區。初始化版塊包含一個文本框、一個選擇框、一個按鈕,用戶在文本框中輸入需要打印的文件,然后進行初始化。調度過程版塊主要為一個表格,用于展示進程調度的詳細過程。文件輸出區版塊用于展示所有文件的打印過程。
2.初始化
用戶首先選擇一個進程,初始默認為A,隨后將要輸出的文件放入初始化版塊的文本框中,然后點擊初始化按鈕,初始化成功,如圖5所示:
對于進程B同進行上述操作,如圖6所示:
3.結果展示
兩個用戶進程初始化完成后,點擊程序運行按鈕,結果展示如圖7和圖8所示:
五、結果分析
下面對圖7內容做簡單分析。如圖9所示:
第1次調度了輸出進程,因為此時輸出井空,所以輸出進程狀態為等待狀態2,此時可用請求塊個數為3。第2次調度進程A,進程A狀態為可執行,可用請求塊個數為3,A將文件0送到輸出井,文件0(“abcd”)長度為4。第3次調度輸出進程,輸出井可用空間為15-4=11,可用請求塊個數變為2,輸出A進程的文件0,如文件輸出區所示,并釋放相關空間。
六、源碼
暫無。
總結
以上是生活随笔為你收集整理的基于C# winform的操作系统课程设计:SPOOLING假脱机输入输出技术模拟的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 52_LSTM及简介,RNN单元的内部结
- 下一篇: c# char unsigned_dll