課程設(shè)計實戰(zhàn):進程調(diào)度、內(nèi)存管理與代碼改造避坑指南)
簡介面向操作系統(tǒng)課程設(shè)計/大作業(yè)的完整參考包適配西安電子科技大學計算機科學與技術(shù)專業(yè)課程設(shè)計場景也適合其他高校學生完成類似實踐環(huán)節(jié)時借鑒。資料定位為參考資料而非定制成品讀者需具備一定編程基礎(chǔ)能夠自行調(diào)試代碼、解決報錯并擴展功能無論初學還是進階都能通過它了解從需求分析、編碼實現(xiàn)到報告整理的全過程。壓縮包采用zip格式整體約41.16MB主要文件包括課程設(shè)計報告、實驗代碼、原始上機截圖與代碼等報告可參考結(jié)構(gòu)撰寫與思路整理代碼可用來復現(xiàn)實驗、排查問題并二次開發(fā)截圖則有助于復盤實際操作過程與運行環(huán)境。目前已吸引608人學習瀏覽覆蓋課程設(shè)計各階段是完成操作系統(tǒng)課程設(shè)計、大作業(yè)或工程實訓初期項目時的實用素材。1. 操作系統(tǒng)課程設(shè)計不是看會的是改會的操作系統(tǒng)課程設(shè)計大概是計算機專業(yè)大作業(yè)里最讓人頭疼的一類進程管理、處理機調(diào)度、內(nèi)存管理、文件系統(tǒng)哪一塊抽出來都夠?qū)憥浊衅虒W進度還卡在“剛講完理論就要交完整實現(xiàn)”。手上這份資源把設(shè)計報告、實驗代碼、原始上機截圖打包在了一起核心價值是給了你一條完整的“從報告到代碼再到驗證”的鏈路而不是零散的幾段代碼。它適合正在做操作系統(tǒng)課程設(shè)計或大作業(yè)的人想搞懂報告怎么寫、代碼怎么組織、現(xiàn)場演示怎么不翻車都能從這里找到參照。我看完第一遍的結(jié)論是直接抄代碼是最虧的用法按報告反推實驗模塊、再動手改代碼才是這類資源真正值回票價的地方。2. 把設(shè)計報告當施工圖先拆模塊再寫代碼2.1 報告先讀“功能需求”和“模塊劃分”這兩頁拿到課程設(shè)計資源包我習慣先不碰代碼把設(shè)計報告從頭翻一遍。報告篇幅通常在八千到一萬字逐字讀完不現(xiàn)實我一般只看功能需求、模塊劃分、測試結(jié)果這三部分功能需求對應“老師驗收時會檢查什么”模塊劃分對應“代碼目錄為什么長這樣”測試結(jié)果對應“截圖里那些終端輸出是怎么來的”。這份資源里的報告功能需求寫得很具體處理機調(diào)度、進程同步、內(nèi)存分配三個核心模塊是有明確輸入輸出描述的。讀的時候拿支筆畫一下哪些函數(shù)是頂層入口哪些函數(shù)是底層實現(xiàn)不同模塊之間是通過什么數(shù)據(jù)結(jié)構(gòu)銜接的。畫完這張邏輯圖再看代碼包里的目錄結(jié)構(gòu)基本能對上號——這就是我說“報告是施工圖”的意思代碼只是施工圖的落地結(jié)果。2.2 按報告反推實驗模塊三塊核心必做兩塊擴展選做絕大多數(shù)操作系統(tǒng)課程設(shè)計都繞著五個經(jīng)典題目轉(zhuǎn)進程同步、處理機調(diào)度、內(nèi)存管理、文件系統(tǒng)、死鎖避免。這份資源覆蓋了其中三個核心模塊外加設(shè)備和文件系統(tǒng)的擴展部分完整模塊拆解我整理成了下面這張表。模塊核心知識點代碼形態(tài)預估代碼量進程同步信號量、PV操作、生產(chǎn)者消費者多線程 共享隊列200-300行處理機調(diào)度先來先服務(wù)、時間片輪轉(zhuǎn)、優(yōu)先級調(diào)度器 進程控制塊300-400行內(nèi)存管理分區(qū)分配、頁式管理、頁面置換內(nèi)存表 分配算法250-350行文件系統(tǒng)擴展目錄結(jié)構(gòu)、空閑塊管理模擬文件系統(tǒng)300-400行死鎖避免擴展銀行家算法、安全性檢測資源分配類200-300行我見過不少同學拿到這類資源后一上來就對著完整代碼埋頭讀讀了兩天還在第一個文件里打轉(zhuǎn)。正確的打開方式是挑一個模塊先吃透比如先從處理機調(diào)度入手因為這個模塊邏輯獨立、不依賴其他模塊的數(shù)據(jù)結(jié)構(gòu)改起來風險最小。三個核心模塊全部看懂、能跑通再決定要不要碰擴展部分時間不夠就果斷放棄擴展核心模塊不出問題才是產(chǎn)出的底線。2.3 工作量倒排四周時間表比想象中更緊課程設(shè)計和普通作業(yè)最大的不同在于有“現(xiàn)場演示”環(huán)節(jié)你得當著老師的面編譯、運行、講解代碼。這就意味著不能把進度壓到最后三天否則一編譯報錯就當場社死。我用這份資源做模擬項目X時按四周做了倒排計劃。第一周只做一件事跑通原始代碼。環(huán)境裝好、編譯通過、把截圖里的演示流程自己在終端里復現(xiàn)一遍這一步是后面所有操作的基礎(chǔ)。第二周選一個核心模塊開始改改之前先寫測試用例保證改動沒有破壞原有功能。第三周做擴展功能和界面優(yōu)化順帶把設(shè)計報告對應章節(jié)補上。第四周是緩沖期專門用來對付那些“昨天還能跑今天突然崩了”的玄學問題同時排練演示流程。提示如果學校給了明確的驗收要求和評分子項倒排計劃要以評分子項為邊界。比如評分要求里處理機調(diào)度占20分那這個模塊至少要投入一周半而不是看代碼順眼就多做、不順眼就跳過。3. 把實驗代碼跑起來環(huán)境搭建、編譯命令與三個參數(shù)坑3.1 開發(fā)環(huán)境與依賴Linux 是默認答案這份資源的實驗代碼是在 Linux 環(huán)境下寫的上機截圖里的終端界面也印證了這一點。如果你平時用的是 Windows別掙扎直接在虛擬機里裝一個 Ubuntu或者用系統(tǒng)自帶的 WSL 都行。我一般用 Ubuntu因為 apt 裝依賴太方便踩坑的解決方案網(wǎng)上也最多。打開終端后先確認三樣東西有沒有裝齊gcc、make、gdb。前兩個是編譯工具鏈的基礎(chǔ)gdb 是調(diào)試工具沒有它你排查段錯誤的時候會痛苦很多。檢查命令很簡單gcc --version make --version gdb --version sudo apt update sudo apt install -y build-essential gdb代碼里如果有額外的第三方庫比如用到了 pthread 多線程編譯命令里要加-lpthread鏈接選項這個后面編譯報錯時最常見的提示就是“undefined reference to pthread_create”。另外提醒一句主機的 Linux 內(nèi)核版本和實驗代碼里的系統(tǒng)調(diào)用之間通常沒有兼容性問題這類課程設(shè)計用的都是最基礎(chǔ)的 POSIX 接口不太會踩到內(nèi)核版本差異。3.2 編譯運行先 make clean 再 make不要硬剛舊產(chǎn)物多文件工程基本都帶 Makefile直接用 make 編譯。但這里有一個很影響效率的操作習慣修改代碼后直接 make如果報出奇怪錯誤先別急著懷疑代碼邏輯先make clean再重新編譯把舊產(chǎn)物清干凈。特別是改過頭文件、動過全局數(shù)據(jù)結(jié)構(gòu)聲明的情況舊的 .o 文件和新代碼混在一起會出現(xiàn)各種匪夷所思的鏈接錯誤。# 進入代碼所在目錄 cd os_course_design # 清掉之前的編譯產(chǎn)物再重新構(gòu)建 make clean make # 查看生成的可執(zhí)行文件 ls -la bin/邏輯說明make clean刪除所有中間目標文件.o和上一次生成的可執(zhí)行文件make從頭開始編譯鏈接。這樣做的好處是避免增量編譯時新舊文件混用導致的詭異問題。參數(shù)說明如果你的課程設(shè)計要求在 Windows 環(huán)境下編譯建議改用 CMake 重新組織工程課程包里沒有附帶 CMakeLists.txt 時自己寫一個只需要十幾行網(wǎng)上模板很多主要注意把 pthread 庫顯式鏈接進去。跑起來之后把報告里描述的功能點挨個驗證一遍。處理機調(diào)度模塊就創(chuàng)建十個進程任務(wù)觀察調(diào)度順序是否和報告一致內(nèi)存管理模塊就申請大塊內(nèi)存再釋放看日志輸出里是否有內(nèi)存泄漏提示。這里花的時間越多、記錄越細后面寫報告和答辯就越省力。3.3 截圖文件怎么用對照時間戳還原現(xiàn)場資源包里附帶原始上機截圖代碼這不是讓你打包交差用的。截圖真正的價值在于還原“代碼跑起來應該長什么樣”——終端提示符長什么樣、調(diào)度過程中的輸出順序、異常情況下的報錯信息。我在做模擬項目X時先用截圖里的終端輸出當期待輸出再跑自己的代碼對比一旦輸出和截圖不一致說明代碼被改動過或者環(huán)境參數(shù)對不上。有個很實用的做法按“功能點輸入數(shù)據(jù)輸出結(jié)果”的格式給截圖重命名。比如一張截圖是“時間片輪轉(zhuǎn) 3個進程 時間片2”另一張是“銀行家算法 5進程 3資源類”答辯時候老師問起來你按名字直接翻到對應圖片比自己臨時在幾十張截圖里翻有效率得多。如果你復現(xiàn)時發(fā)現(xiàn)輸出和截圖有出入先檢查輸入的初始參數(shù)是否一致調(diào)度算法類實驗對輸入數(shù)據(jù)極敏感初始狀態(tài)不同結(jié)果就不同不一定是代碼有問題。3.4 三個最容易出錯的參數(shù)坑這類系統(tǒng)級實驗代碼里參數(shù)配置集中在頭文件或配置文件里我第一次跑通后改代碼時就踩了好幾個坑。挑三個最高頻的說第一個是線程棧大小。默認??臻g是 8MB如果實驗代碼里用了深度遞歸或者大數(shù)組分配運行時直接段錯誤Segmentation fault。解決方法是ulimit -s查看當前棧大小限制或者用pthread_attr_setstacksize顯式設(shè)置線程棧大小。第二個是時間片長度。處理機調(diào)度實驗里時間片一般用static int time_slice 2;定義單位是秒的話程序跑得很慢單位是毫秒的話肉眼根本來不及看輸出。正確的做法是提供命令行參數(shù)運行時可配置代碼里寫死很容易翻車。第三個是日志緩沖區(qū)大小。有些代碼用printf直接刷屏有些內(nèi)部維護循環(huán)緩沖區(qū)后者寫滿了會丟日志表現(xiàn)是“跑到第N條輸出就斷了”。這個坑最隱蔽因為不是編譯錯誤、不是段錯誤就是輸出不對排查了半天才發(fā)現(xiàn)是緩沖區(qū)寫滿了。處理辦法是把日志級別降為只打印關(guān)鍵節(jié)點或者把日志寫入文件而不是終端。# 運行調(diào)度實驗指定時間片為 2 個時間單位 ./bin/scheduler -q 2 -p 5 # 運行進程同步實驗指定生產(chǎn)者/消費者數(shù)量 ./bin/pc_sync -p 3 -c 3 -b 5參數(shù)說明-q是時間片長度-p是進程或生產(chǎn)者數(shù)量-c是消費者數(shù)量-b是緩沖區(qū)容量。沒有統(tǒng)一標準每個代碼包的命令行參數(shù)不一樣運行前先看 README 或者跑一下./bin/scheduler --help。4. 改造代碼從“能跑”到“能答辯”的四個切入點4.1 換調(diào)度算法時間片輪轉(zhuǎn)改多級反饋隊列課程設(shè)計評分細則里最常出現(xiàn)的一句話是“在原有功能基礎(chǔ)上有所擴展”。原樣提供的代碼只能拿基礎(chǔ)分想拿高分就要動刀。第一個推薦切入點是把時間片輪轉(zhuǎn)Round Robin改成多級反饋隊列MLFQ。從代碼量看這個改動在 200 行左右不需要改數(shù)據(jù)結(jié)構(gòu)設(shè)計只是增加隊列數(shù)量、改變?nèi)蝿?wù)在不同隊列間的遷移邏輯性價比非常高。/* 多級反饋隊列核心邏輯優(yōu)先級從高到低時間片逐級翻倍 */ #define MAX_Q_LEVEL 4 int cur_q_level 0; // 當前隊列級別0 為最高優(yōu)先級 void mlfq_enqueue(PCB *task) { if (task-time_slice_used time_slice_per_level[task-q_level]) { // 時間片用滿降到下一級隊列并重置計數(shù)器 task-q_level (task-q_level 1 MAX_Q_LEVEL) ? task-q_level 1 : MAX_Q_LEVEL - 1; task-time_slice_used 0; } task_list[task-q_level].push_back(task); }邏輯說明time_slice_per_level是一個數(shù)組定義每一級隊列對應的時間片長度常見的配置是 [1, 2, 4, 8]搶占式多級隊列還要實現(xiàn)“高優(yōu)先級隊列有新任務(wù)時立刻搶占 CPU”。代碼里的MAX_Q_LEVEL控制隊列數(shù)量實際改的時候建議先用 3 級隊列驗證邏輯跑通后再擴展到 4 級。參數(shù)說明task-q_level是進程當前所在隊列級別task-time_slice_used記錄該進程在當前隊列里已經(jīng)用過的時間片。這兩個字段需要在 PCB 結(jié)構(gòu)體里顯式新增改完記得在初始化函數(shù)里置零不然未經(jīng)初始化的變量值直接進隊列會亂掉。4.2 給同步模塊加一把“自旋鎖”進程同步模塊大多數(shù)是“生產(chǎn)者-消費者”或“讀者-寫者”問題。原始代碼一般用二元信號量實現(xiàn)互斥但信號量的實現(xiàn)細節(jié)比如 waiting 隊列不一定暴露出來。另一條擴展路徑是新增自旋鎖spinlock的封裝用于驗證互斥訪問控制。/* 簡單自旋鎖使用 GCC 內(nèi)置原子操作實現(xiàn) */ typedef struct { volatile int flag; } spinlock_t; void spinlock_init(spinlock_t *lock) { __sync_lock_release(lock-flag); // 初始化為 0未上鎖 } void spinlock_lock(spinlock_t *lock) { while (__sync_lock_test_and_set(lock-flag, 1)) { // 忙等待CPU 空轉(zhuǎn)直到鎖被釋放 #ifdef _DEBUG printf(thread %d spinning...\n, pthread_self()); #endif } } void spinlock_unlock(spinlock_t *lock) { __sync_lock_release(lock-flag); }邏輯說明__sync_lock_test_and_set是 GCC 內(nèi)置的原子交換指令它把目標變量設(shè)為 1同時返回舊值。如果舊值是 0說明鎖是空閑的當前線程成功持鎖如果舊值是 1說明鎖被別的線程占著線程在 while 循環(huán)里自旋。自旋鎖跟信號量最大的差別是自旋鎖不讓線程睡眠而是忙等適合臨界區(qū)極短的場景。參數(shù)說明volatile修飾符不能省它告訴編譯器這個變量可能被多個線程同時修改不要對它做寄存器級優(yōu)化。_DEBUG宏控制是否輸出自旋日志默認不開因為開啟后大量 printf 會拖慢程序運行反而影響實驗結(jié)果。4.3 給關(guān)鍵操作加日志用時間戳定位調(diào)度順序答辯時老師最常問的一句話是“你的調(diào)度順序是依據(jù)什么設(shè)計的”如果你只說“按時間片輪轉(zhuǎn)”那和沒做一樣。加分回答是調(diào)出帶時間戳和線程號的日志逐條分析每個切換點。原始代碼不一定自帶精細日志自己加也不復雜/* 調(diào)度器日志記錄每個進程被調(diào)度的時間點和當前運行狀態(tài) */ void log_schedule_event(PCB *task, const char *event) { struct timeval tv; gettimeofday(tv, NULL); /* 時間戳精確到毫秒格式: [1234.567] pid3 RUNNING */ printf([%ld.%03ld] pid%d state%s\n, tv.tv_sec % 10000, tv.tv_usec / 1000, task-pid, event); }邏輯說明gettimeofday返回當前時間tv_sec是秒、tv_usec是微秒把微秒除以 1000 轉(zhuǎn)成毫秒后拼在秒后面就能得到形如[1234.567]的高精度時間戳。加日志的位置一般是進程狀態(tài)切換的三處從 ready 變 running、從 running 變 waiting、從 running 變 terminated。參數(shù)說明事件類型字符串event用常量而不是魔法字符串比如調(diào)用時寫log_schedule_event(task, RUNNING)而不要直接傳running。日志輸出建議默認關(guān)閉用一個全局日志級別變量控制答辯前調(diào)高級別打印完整過程平時調(diào)低避免刷屏。4.4 把輸入?yún)?shù)做成“可配置”而不是改一行編譯一次這類課程設(shè)計的代碼很多的實現(xiàn)問題在于參數(shù)寫死在代碼里改一次要重新編譯。我見過最夸張的是在一個循環(huán)里改一個數(shù)字重新編譯了十幾次。改成配置文件或命令行參數(shù)解析是性價比最高的“精致化”改動代碼量不多但答辯時能講的故事多。一個簡易但夠用的做法是用getopt_long解析命令行參數(shù)時間片、進程數(shù)量、調(diào)度策略都從命令行傳入./bin/scheduler --algorithm mlfq --time-slice 2 --process 5 --queue-level 3參數(shù)說明--algorithm指定調(diào)度算法--time-slice指定基礎(chǔ)時間片--process指定進程數(shù)量--queue-level指定多級隊列層數(shù)。實現(xiàn)它只需要在 main 函數(shù)里加一個getopt_long調(diào)用把解析結(jié)果賦給全局配置變量。這樣做的好處是答辯時可以直接現(xiàn)場演示不同參數(shù)組合下的調(diào)度結(jié)果老師問你“時間片從2改成3會有什么影響”你一條命令跑出來給他看比任何口頭解釋都有說服力。5. 操作系統(tǒng)課程設(shè)計避坑五條血淚教訓5.1 編譯通過但運行閃退問題多半出在內(nèi)存分配現(xiàn)象make 順利完成./bin/scheduler一運行終端里什么都沒有直接退回 shell 提示符。原因這是典型的運行時崩潰多半是malloc之后沒有檢查返回值就直接使用。在多進程調(diào)度模擬里如果創(chuàng)建進程控制塊時內(nèi)存分配失敗進程表就出現(xiàn)空指針隨后對空指針取字段觸發(fā)段錯誤。段錯誤發(fā)生時程序被內(nèi)核直接殺掉看起來就是“閃退”。解決先用gdb跑一次gdb ./bin/scheduler然后輸入run崩潰后輸入bt看調(diào)用棧定位到具體哪一行出了問題。修復方式是在每次分配內(nèi)存后加判斷分配失敗就打印錯誤信息并退出而不是繼續(xù)往下走。代碼量不大但能救你很多次。5.2 多線程程序輸出順序詭異和報告里寫的不一致現(xiàn)象生產(chǎn)者消費者實驗里消費者先打印了“正在消費”生產(chǎn)者后打印“正在生產(chǎn)”但報告里寫的是“先生產(chǎn)后消費”。原因多線程程序的執(zhí)行順序本來就不確定printf 輸出只能反映“調(diào)用 printf 那一刻的狀態(tài)”不能反映“線程間的執(zhí)行順序”。報告里寫的“先生產(chǎn)后消費”是邏輯上的正確順序但線程切換可能在produce函數(shù)執(zhí)行到一半時發(fā)生輸出自然就穿插了。解決輸出順序不要依賴 printf 的調(diào)用順序。在關(guān)鍵操作前后加狀態(tài)標記比如設(shè)置一個item-is_consumed標志位最后統(tǒng)一統(tǒng)計“正確消費數(shù)”和“錯誤消費數(shù)”以統(tǒng)計結(jié)果為準。更省事的辦法是讓日志輸出帶線程 ID你就能從時間戳上還原真實的執(zhí)行順序而不是靠肉眼對比字符串先后。5.3 時間片輪轉(zhuǎn)跑出來像先來先服務(wù)設(shè)置小了又卡死現(xiàn)象調(diào)度模塊設(shè)時間片為 100三個進程的輸出順序和先來先服務(wù)完全一樣設(shè)成 1程序運行好幾秒都結(jié)束不了。原因時間片太長時每個進程在自己的一大段時間內(nèi)直接運行完根本沒發(fā)生切換效果等價于 FCFS。時間片太短時進程頻繁切換上下文開銷巨大程序總耗時飆升。很多課程設(shè)計代碼的時間片單位是“次循環(huán)”不是秒設(shè)置不合適會出現(xiàn)這種“兩個極端都不對”的結(jié)果。解決把時間片作為運行參數(shù)而不是編譯期常量在不同的輸入規(guī)模下多測幾組選一個“輸出順序有變化但程序能在 1 秒內(nèi)跑完”的中間值。比如進程數(shù) 5 個、每個進程需要 10 個單位時間時時間片設(shè) 3 或 4 最合適。另外建議記錄一下不同時間片下的平均等待時間答辯時這個數(shù)據(jù)比“我試了很多次終于跑通了”有說服力得多。5.4 改了代碼之后 git diff 看懵了找不到自己改了哪幾行現(xiàn)象課程設(shè)計拖了兩周代碼改了幾十處答辯前檢查時發(fā)現(xiàn)自己都記不清改過哪里。原因沒有版本管理。改的時候東一榔頭西一棒子今天加了個信號量明天覺得不對刪掉了后天又換了一種實現(xiàn)。到后期代碼里堆了三四種互相矛盾的邏輯片段。解決拿到資源包的第一件事就是git init并提交一次原始版本之后每完成一個功能點提交一次提交信息寫清楚改動內(nèi)容比如feat: add MLFQ queue level或fix: resolve NULL pointer in memory alloc。答辯前憑提交記錄就能列出一張完整的“改動清單”老師問起來照著說就行。如果學校課程設(shè)計有“中期檢查”這份提交記錄本身就是一個很好的進度證明。5.5 上機演示前十分鐘編譯報錯忙中出錯越改越亂現(xiàn)象演示前做最后一次完善改了一個參數(shù)重新編譯鏈接報錯手忙腳亂改成另一處錯誤更多了最后交上去一份編不過的代碼。原因這是時間管理的問題本質(zhì)上是“最后改動”和“驗證時間”之間的窗口太短。臨時改代碼改完沒有做回歸測試連編譯驗證都沒有就上場了。而演示時是不能看手機的現(xiàn)場編不過基本等于這次課程設(shè)計白做。解決演示前一天晚上代碼凍結(jié)不再動任何邏輯代碼。演示當天只做三件事make clean make跑一遍預演的測試用例打開演示用的終端窗口。如果演示時真的編不過最穩(wěn)的兜底是切回上一次git commit的版本編譯通過后再解釋“我正在做優(yōu)化但是優(yōu)先保證核心功能可以運行”。保證能跑的版本和進化的版本分開是這類系統(tǒng)工程實踐作業(yè)最基本的自救手段。6. 給課程設(shè)計加一道自動化驗收用腳本跑完所有測試答辯前的最后階段與其一遍遍手敲命令驗證功能不如把測試過程固化成一個腳本一鍵跑完。這個習慣是我在被某次驗收現(xiàn)場“手動輸錯參數(shù)導致實驗失敗”坑過之后形成的。從那以后每次做課程設(shè)計或大作業(yè)我都會提前寫好run_tests.sh把該驗證的功能點全部自動化覆蓋#!/bin/bash # 自動化驗收腳本覆蓋調(diào)度、同步、內(nèi)存三個核心模塊 PASS0 FAIL0 # 測試 1: 處理機調(diào)度 - 時間片輪轉(zhuǎn) output$(./bin/scheduler -q 2 -p 5 2/dev/null) if echo $output | grep -q PID 1.*RUNNING; then echo [PASS] scheduler round-robin PASS$((PASS1)) else echo [FAIL] scheduler round-robin FAIL$((FAIL1)) fi # 測試 2: 進程同步 - 生產(chǎn)者消費者 output$(./bin/pc_sync -p 3 -c 3 -b 5 2/dev/null) if echo $output | grep -q Consumed items: [0-9]; then echo [PASS] sync producer-consumer PASS$((PASS1)) else echo [FAIL] sync producer-consumer FAIL$((FAIL1)) fi # 測試 3: 內(nèi)存管理 - 分配釋放 output$(./bin/mem_mgr -s 1024 -a 128 2/dev/null) if echo $output | grep -q Allocation succeeded; then echo [PASS] mem allocation PASS$((PASS1)) else echo [FAIL] mem allocation FAIL$((FAIL1)) fi echo echo Total: $((PASSFAIL)), Passed: $PASS, Failed: $FAIL邏輯說明腳本對每個模塊執(zhí)行一次帶代表性參數(shù)的運行然后用grep檢查輸出中是否包含預期關(guān)鍵字。匹配到就標記 PASS匹配不到就標記 FAIL最后輸出統(tǒng)計數(shù)據(jù)相當于一版精簡的冒煙測試。測試命令里的2/dev/null是丟棄標準錯誤輸出避免腳本被無關(guān)的警告日志干擾。參數(shù)說明每個測試用例的輸入?yún)?shù)要和設(shè)計報告里寫的一致比如調(diào)度模塊用時間片 2、5 個進程內(nèi)存模塊用 1024KB 內(nèi)存、按 128KB 分區(qū)。參數(shù)不一致會導致測試結(jié)果和報告對不上反過來又增加答辯時的解釋成本。要新增測試項就把“運行命令 預期輸出關(guān)鍵字”加到腳本里改造擴展功能時也順手補一條測試。這個腳本還有另一個用法每次跑完自動化測試把輸出結(jié)果存成文件對著結(jié)果寫報告。報告里的測試部分不再需要復述“我準備了3組測試”而是直接列出跑出的數(shù)據(jù)哪里過了哪里沒過一目了然。從那以后我每次做這種系統(tǒng)級課程設(shè)計都強制自己先寫測試腳本再做功能開發(fā)測試先行可以幫你把代碼保持在一個“隨時能跑”的狀態(tài)。這算是做過幾次操作系統(tǒng)課程設(shè)計后最值錢的一個習慣希望幫到你。本文還有配套的精品資源點擊獲取