
本文代碼地址鏈接:
操作系統課程設計Flask后端代碼:https://github.com/lxy764139720/OS_experiment
操作系統課程設計Vue前端代碼:https://github.com/lxy764139720/os_experiment_vue
多道程序系統中,進程與進程之間存在同步與互斥關系。當就緒進程數大於處理機數時,需按照某種策略決定哪些進程先占用處理機。在可變分區管理方式下,采用首次適應算法實現主存空間的分配和回收。
本程序模擬實現處理機調度和內存分配及回收機制,並通過可視化界面觀察進程的運行流程與情況。為了實現算法與界面的解耦合,以及繪制更加優美的界面,本實驗設計了前后端分離的架構,在后端使用Python的Flask框架實現相關算法並設計通信接口,在前端使用Vue.js框架及Element UI實現可視化界面,前后端通過Axios框架進行通信。
起步
1. clone git倉庫
git clone https://github.com/lxy764139720/OS_experiment
git clone https://github.com/lxy764139720/os_experiment_vue
2. 安裝環境
pip install flask
pip install flask_cors
3. 啟動后端程序
cd <PREFIX_PATH>/OS_experiment
python app.py
4. 啟動前端程序
cd <PREFIX_PATH>/os_experiment_vue
npm install
npm run serve
5. 打開頁面 http://localhost:8080/
功能
為了實現對處理機調度算法和內存分配回收算法的模擬,本程序允許用戶進行如下操作:創建進程、掛起/解掛進程、運行進程、刷新。
此外,用戶可以通過前端界面實時查看每個操作帶來的效果:全部進程信息表、內存分區表、處理機中的進程隊列、后備隊列、掛起隊列、進程的前驅后繼圖。
使用
1. 創建進程
創建進程需要用戶輸入所需時間、所需內存,選擇優先級、進程屬性和前驅進程。
前驅進程的選擇會根據進程屬性自動切換是否禁用,選擇同步進程時可以選擇多項已有進程作為前驅進程。
進程信息填寫不完整,或內存超出主存空間的最大容量時,會進行相應報錯提示。
選擇同步進程但為選擇前驅進程時,會自動將進程切換為獨立進程,並顯示提示信息。

2. 掛起/解掛進程
掛起/解掛進程分頁分為兩列,左邊為未掛起進程列表,右邊為掛起進程列表。
可以多選進程並點擊下方箭頭進行掛起和解掛操作。
已經結束的進程不會出現在列表中。

3. 刷新/運行
模擬進程運行一個時間單位。
4. 查看內存
內存空間分頁中展示了當前所有的內存分區表,其中進程號-1表示操作系統占用內存,用橘黃色表示,-2表示未分配內存用綠色表示。
通過修改后端的 Cofig.py 配置文件,可以很方便的改變內存大小。

5. 查看處理機
處理機分頁中顯示了所有處理機中分配的進程號,這些進程都處於內存中的活動就緒狀態,可以直接運行。

通過修改后端的 Cofig.py 配置文件,可以很方便的改變處理機的個數,下圖所示為5台處理機分配三個進程時的情況。

6. 查看后備隊列
后備隊列分頁中顯示了當前因內存空間不足,無法置入內存運行的進程號。
7. 查看掛起隊列
掛起隊列分頁中顯示了掛起隊列中的進程情況。
結構

本程序為進程設計了六種狀態模型:共有<CREATE: 0>、<ACTIVE_READY: 1>、<STATIC_READY: 2>、<RUNNING: 3>、<SUSPENDING: 4>、<EXIT: 5>六種狀態;兩種屬性:< INDEPENDENT: 0>、< SYNCHRONIZED: 1>。
程序為處於活動就緒狀態、掛起狀態和靜止就緒狀態的進程分別設置了就緒隊列、掛起隊列和后備隊列,按先進先出的順序存儲進程的進程號。
另外設置一個進程隊列存儲所有的進程對象,以便根據進程號獲取進程對象。

進程調度流程
1. 進程創建
-
【輸入】 進程的運行時間,所需內存空間,優先權,狀態,屬性(,前驅)
-
【限制】 進程所需內存空間不能超過總內存空間減去操作系統所需的內存空間
-
對全部進程隊列末尾的進程號+1獲取新進程的進程號,設置狀態為進程創建,初始化進程PCB並加入全部進程隊列的隊尾
-
為之前的所有進程設置后繼進程
-
檢查可分配內存空間,決定進程進入后備隊列或就緒隊列
-
如果可分配內存空間足夠,按首次適應算法將進程插入內存(就緒隊列),並為就緒隊列中的所有進程分配處理機
-
如果可分配內存空間不足,將進程加入到后備隊列的隊尾
2. 進程掛起
-
對處於活動就緒(內存中)的進程,從處理機的進程隊列中移出進程,釋放內存空間,合並未分配的內存空間,為就緒隊列中的所有進程重新分配處理機
-
對於處於靜止就緒(后備隊列中)狀態的進程,從后備隊列中移出進程
-
加入到掛起隊列的隊尾
-
改變程序狀態為靜止就緒
3. 進程解掛
-
從掛起隊列中移除進程
-
檢查可分配內存空間,決定進程進入后備隊列或就緒隊列
-
如果可分配內存空間足夠,按首次適應算法將進程插入內存(就緒隊列),並為就緒隊列中的所有進程分配處理機
-
如果可分配內存空間不足,將進程加入到后備隊列的隊尾
4. 進程運行
-
檢查主存空間和處理機上是否有可運行程序
-
運行所有處理機上的第一個進程
-
根據可分配主存空間的大小,自動將后備隊列中的進程移入主存,同時重新為就緒隊列中的所有進程分配處理機
5. 進程結束
- 進程運行后,若所需時間為0,則設置狀態為進程結束
內存調度算法——內存空間首次適應分配
程序中管理內存空間的類包含兩個屬性:就緒隊列和內存分區表。
就緒隊列中存儲所有處於內存空間中的進程號,內存分區表中以[PID, 起址, 長度, 狀態]的形式存儲內存塊,並按起始地址進行升序排序。
內存分區表中對應存儲的PID,OS對應的內存表項用-1表示,未分配內存表項用-2表示;狀態有<UNASSIGNED: 0>、<ASSIGNED: 1>、<OS_ASSIGNED: 2>三種。
1. 檢查是否可分配內存空間
對於給定的進程,從內存分區表中依次查找,如果找到一塊未分配內存並且該內存塊的大小大於等於進程所需的內存,就返回對應內存塊的序號,否則返回-1。
2. 分配內存空間
-
首先檢查是否可分配內存空間,獲取可分配內存塊的序號
-
如果該塊內存與進程所需內存的大小恰好相等,直接將該塊內存的進程號改為該進程的進程號,狀態設置為已分配
-
如果該塊內存小於進程所需內存的大小,則在其后面插入一個未分區內存塊,新內存塊的起始地址為上一內存的起始地址加上進程所需內存大小,大小為上一內存的大小減去進程所需內存大小。將上一內存塊的進程號、內存塊大小和狀態改為該進程的進程號、內存大小和已分配
3. 從主存空間中移除進程
-
首先從處理機中移除進程
-
遍歷內存分區表,找到進程對應存儲的內存塊的序號
-
將該內存塊的進程號設置為-2,狀態設置為未分配
-
從就緒隊列中移除進程號
-
合並未分配內存空間
4. 合並未分配內存空間
-
遍歷內存分區表,找到第一塊未分配的內存空間
-
繼續遍歷,將緊鄰的后續未分配內存空間加入要合並的內存列表,直到下一塊內存空間是已分配的
-
對於要合並的內存列表,從內存空間中依次倒序彈出(避免彈出時超過列表的最大索引),將彈出內存塊的大小加入找到的第一塊未分配的內存空間中
5. 檢查是否有可運行進程
處理機調度算法
程序中的處理機類包含兩個屬性:處理機個數和處理機上的進程號列表。
處理機的個數通過配置文件進行設置,處理機上的進程號列表初始化為與處理機個數相同的空列表。
處理機類與主存空間類綁定,其中的方法只能通過主存空間對象進行調用,相當於主存空間的內部類。
1. 處理進程運行
-
對所有處理機進行遍歷,獲取每個處理機上的第一個進程
-
對於該進程,判斷其是否為獨立進程、前驅進程全部完成的同步進程、前驅進程不是剛在本次運行的不同處理機中結束的同步進程這三種情況之一
-
對於b中的判斷結果為是的進程,運行時間減少一個單位(模擬進程運行)
-
對於b中的判斷結果為否的進程,不做操作,進入下一循環
-
如果該進程運行后恰好結束,則將其從處理機中移除,並加入本輪結束的進程列表,供b中的判斷使用
2. 從處理機中移除進程
- 在處理機進程列表中查找該進程,如果找到,移除該位置的進程
3. 為進程分配處理機
-
在主存空間類中構建兩個字典,一個存儲獨立進程以及前驅已經完成的同步進程,另一個存儲同步進程
-
遍歷就緒隊列,為每個進程設置狀態為活動就緒,並根據進程屬性加入到a中設置的兩個字典中
-
分別對兩個字典按進程優先權降序排列
-
將兩個字典按先獨立后同步的順序合並為一個列表
-
循環為每個處理機分配進程
類設計
1. PCB類
保存進程的狀態信息,可以設置進程狀態,添加后繼進程,運行進程。

2. PCBQueue類
保存所有的進程對象,可以獲取新增進程的進程號,通過進程PID獲取進程對象,添加新進程,設置某一進程的后繼進程。

3. BackupQueue類
后備隊列,可以添加進程,有可分配內存空間時自動移出PCB,手動移出PCB

4. HangingQueue類
掛起隊列,可以添加和移出進程

5. MainMemory類
主存空間類,可以檢查是否可分配空間,插入進程,檢查是否有可運行進程,運行進程,移除進程,合並主存空間,為處理機分配進程。

6. Processor類
處理機類,可以運行進程,移除進程,為處理機分配進程,其中的方法只能被MainMemory類對象調用,相當於其內部類。

