境到軟中斷:一份95分操作系統(tǒng)實驗報告全解析)
簡介西南交通大學操作系統(tǒng)實驗報告完整收錄胡曉鵬老師課程的六個實驗涵蓋Linux環(huán)境與Shell命令、C程序編譯調試、/proc內核狀態(tài)觀測、軟中斷通信、進程調度及線程同步等主題適合正在學習操作系統(tǒng)或Linux編程的本科生對照參考。報告基于2020—2021學年軟件工程專業(yè)真實作業(yè)評分達95分不僅給出每次實驗的目的、步驟與總結還詳細展示了cd、ls、ps、chmod等命令用法以及vi、gcc、gdb工具鏈操作和cpuinfo、meminfo、loadavg、interrupts等內核文件的編程觀測方法。壓縮包內共1個docx文檔約1.48MB目錄結構按實驗編號清晰劃分方便直接查閱或借鑒格式。該資源已有401人學習對需要完成同類實驗、撰寫實驗報告或理解Linux內核抽象的同學具有較高參考價值可直接作為實驗流程梳理、命令細節(jié)核對和報告排版范本使用。1. 一份 95 分的操作系統(tǒng)實驗報告從讀懂 Linux 到跑通軟中斷某高校軟件工程專業(yè)的操作系統(tǒng)課設里有一份評分 95 的實驗報告覆蓋了 Linux 環(huán)境與 C 語言調試、/proc 文件系統(tǒng)觀測、軟中斷通信、進程調度、線程同步、內存管理、系統(tǒng)調用和缺頁統(tǒng)計八個實驗。這份報告值得參考的點不在于每個實驗都跑通了而在于它把「代碼演示」和「機制理解」放在了一起——比如用 Ctrl\ 觸發(fā) SIGQUIT 來觀察父子進程的終止順序比如通過 /proc/cpuinfo 和 /proc/meminfo 去對應課堂上講的內核變量。對于正在做操作系統(tǒng)課設或準備 OS 方向面試的人來說這套實驗編排剛好覆蓋了進程管理、內存管理、信號機制、系統(tǒng)調用四大主線。本文就按這份報告的結構拆開講從環(huán)境準備到 /proc 觀測再到軟中斷和進程調度最后落到內存管理、系統(tǒng)調用和缺頁統(tǒng)計并給出驗收和答辯的方法。如果你是照著課設要求逐項做可以直接按這個順序推進。2. 實驗環(huán)境準備Linux 目錄結構、Shell 命令與 gcc/gdb 調試鏈操作系統(tǒng)實驗的第一步永遠是環(huán)境。報告中的實驗 1 花費了大量篇幅在「熟悉 Linux 界面與目錄結構」和「在終端里跑通一個 C 程序」上。這一步看起來基礎實際決定了后面七個實驗能不能順利推進尤其是 fork、signal 這類依賴進程機制的程序必須在一個能穩(wěn)定運行的環(huán)境里調試。2.1 Linux 目錄結構先搞清楚東西放在哪報告里記錄了一個很直觀的觀察Linux 和 Windows 的目錄組織方式完全不同。Windows 用盤符C:、D:區(qū)分存儲區(qū)域Linux 則是單一根目錄/用正斜杠/分割路徑。初次接觸時容易混淆的點在于/bin存放最常用的命令ls、cp、mv/home是用戶主目錄的根/etc保存系統(tǒng)配置文件/proc是虛擬文件系統(tǒng)運行時的內核信息都在這里。報告特別標注了/proc下的數字目錄——每個數字對應一個正在運行的進程 PID里面是進程的運行時信息。我把這些目錄的用途整理成一張速查表方便對照目錄路徑主要用途實驗中的使用場景/bin常用命令存放處ls、cp、mv的執(zhí)行文件/home用戶主目錄存放實驗代碼文件/proc虛擬文件系統(tǒng)內核狀態(tài)接口實驗 1、實驗 7 讀取系統(tǒng)信息/tmp臨時文件編譯中間文件的臨時輸出/lostfound異常關機后的文件恢復區(qū)一般用不到做實驗時一個典型的操作流程是用cd進入工作目錄用ls確認文件存在用mv重命名文件。報告里提到一個細節(jié)想修改已編輯好的文件名在終端用mv agrc.c oslab.1.c即可如果用 vim 編輯也可以在保存時用:w new_filename直接另存為新文件。兩個方法都能用前者是 Shell 層的操作后者是編輯器內的操作場景不同選擇不同。2.2 Shell 命令與管道重定向不只是敲命令報告在實驗 1 中列出的命令包括cd切換目錄、ls列出文件、ps查看進程狀態(tài)、chmod修改權限以及 redirect重定向和 pipe管道機制。這里的核心在于Shell 不只是逐條執(zhí)行命令還能通過重定向把輸出寫入文件通過管道把前一個命令的輸出作為后一個命令的輸入。比如ps -ef | grep test就是查看進程列表中是否有 test 程序的經典組合ls -l filelist.txt則把目錄列表保存到文件里。這兩個機制在后續(xù)實驗中頻繁用到——軟中斷實驗里需要觀察進程 PID 和父子關系ps和grep的組合能快速確認進程是否被正確創(chuàng)建和終止。2.3 gcc 編譯與 gdb 調試把 C 程序跑起來報告中的實驗流程是用 vim 寫代碼按i進入編輯模式寫完按ESC返回一般模式再用:wq保存退出最后用gcc編譯。這里有個常見錯誤初次使用 vim 的人經常不知道如何進入編輯模式或者按了ESC后直接輸入字符導致命令沖突。報告記錄的步驟把這條路走通了進入 vim 后按i、o、a、r中的任一鍵都能進入編輯模式ESC返回后:開頭的是命令模式:wq是保存并退出。編譯階段gcc是最直接的編譯器gcc -o test test.c生成可執(zhí)行文件gcc -g test.c -o test帶調試信息生成。要比較 gcc、gdb、codeblocks 的區(qū)別核心在于 debug 能力gdb 支持設置斷點、單步執(zhí)行、查看變量值codeblocks 是圖形化 IDE把編譯和調試集成了但對遠程環(huán)境如虛擬機中的 Linux不友好。報告中提到一個血淚經驗在 Windows 的 IDE 里寫好帶fork()的代碼復制到 Linux 后報錯——因為 Windows IDE 默認的頭文件和庫不含 POSIX 的fork()。從那以后我的習慣是涉及進程、信號、文件鎖的代碼直接在 Linux 環(huán)境里寫不再跨環(huán)境搬運。注意gcc編譯時建議加-Wall參數查看全部警告。fork 相關的代碼如果出現(xiàn)隱式聲明警告通常是因為缺少#include unistd.h這是最容易被忽略的問題。2.4 網絡配置的三種模式與踩坑記錄實驗報告里記錄了一次網絡故障排查虛擬機裝好后配置沒問題但網絡連接一直失敗。排查后定位到是虛擬機的網絡模式選錯了并借此把三種模式學明白了橋接模式是讓虛擬機像局域網里的一臺獨立主機可以互訪也可以上網但 IP 容易沖突NAT地址轉換模式是虛擬機通過母機代理訪問外網外部網絡訪問不到虛擬機主機模式host-only則是虛擬機和宿主機形成獨立網絡虛擬機無法訪問互聯(lián)網。最終選擇 NAT 模式既避免 IP 沖突又能上網。這里給初學者一個建議操作系統(tǒng)實驗大多不需要外部網絡但需要宿主機與虛擬機之間傳輸代碼文件。用 NAT 模式配合xftp或scp是最省心的方案如果只有一臺機器做實驗host-only 也夠用只是不能在線查資料。如果遇到網絡不通先 ping 網關再確認虛擬網卡是否啟用最后檢查防火墻——三個步驟能排除九成問題。3. /proc 文件系統(tǒng)觀測內核變量的黑匣子怎么打開實驗 1 和實驗 7 把重點放在了/proc文件系統(tǒng)上。/proc是 Linux 內核向用戶態(tài)暴露狀態(tài)信息的虛擬文件系統(tǒng)讀取它以獲取 CPU 信息、內存狀態(tài)、系統(tǒng)負載、中斷統(tǒng)計等數據。它之所以重要是因為操作系統(tǒng)課上學到的很多概念如進程狀態(tài)、內存分配、系統(tǒng)節(jié)拍都以文件的形式反映在這里且可以隨時查閱。3.1 /proc/cpuinfo、version、stat、uptime 的觀測方法報告中明確給出了用程序觀測的四項內容/proc/cpuinfo提供 CPU 型號、核數、頻率等信息/proc/version顯示內核版本號例如 Linux 3.10.0-1160包含內核編譯信息/proc/stat保存系統(tǒng)啟動以來的 CPU 統(tǒng)計和進程切換數據/proc/uptime返回系統(tǒng)開機時長。觀測這些文件最簡單的方式是cat /proc/cpuinfo但課程設計要求「編程實現(xiàn)觀測」這就涉及 C 語言的文件讀取操作核心代碼如下#include stdio.h #include string.h int main() { FILE *fp; char line[256]; fp fopen(/proc/version, r); if (fp NULL) { perror(open /proc/version failed); return 1; } while (fgets(line, sizeof(line), fp) ! NULL) { printf(%s, line); } fclose(fp); return 0; }這段代碼本質上就是打開文件、逐行讀取、打印。但設計要點在于/proc下的文件是內核動態(tài)生成的虛擬文件讀取它不需要特殊權限部分需要 root但打開失敗時要用perror輸出錯誤原因。如果報No such file or directory說明當前系統(tǒng)的內核版本不包含該節(jié)點例如/proc/filesystems在部分精簡內核中不存在——此時需要檢查內核配置而不是改代碼。將fopen的文件路徑換成/proc/cpuinfo、/proc/stat、/proc/uptime即可觀測其余項目。3.2 meminfo、loadavg、interrupts、filesystem 的編程觀測報告要求編程實現(xiàn)對/proc/meminfo、/proc/loadavg、/proc/interrupts、/proc/filesystems的觀測。這四個文件的含義分別是meminfo描述系統(tǒng)內存總量、空閑量、緩存量對應課程中的內存管理章節(jié)loadavg顯示 1 分鐘、5 分鐘、15 分鐘的平均負載interrupts記錄各中斷號的發(fā)生次數對應中斷機制filesystems列出當前內核支持的文件系統(tǒng)類型。通常的做法是寫一個通用的讀取函數傳入路徑參數即可如下所示void show_file(const char *path) { FILE *fp fopen(path, r); char line[256]; if (!fp) { perror(path); return; } while (fgets(line, sizeof(line), fp) ! NULL) { printf(%s, line); } fclose(fp); } int main() { show_file(/proc/meminfo); show_file(/proc/loadavg); show_file(/proc/interrupts); show_file(/proc/filesystems); return 0; }這里有一個容易忽略的點/proc文件是偽文件讀取時fseek、rewind等操作沒有實際意義每次讀取都是內核實時生成的快照。所以不要在代碼里做「定位到文件末尾再讀回開頭」的操作直接順序讀取即可。3.3 從 jiffies 理解內核時間基準報告在實驗總結中記錄了一個有意思的思考為什么 Linux 的時間統(tǒng)計是基于「節(jié)拍」而不是「秒」答案是內核使用jiffies作為全局時間基準——內核每過一個節(jié)拍通常 1/250 秒或 1/100 秒jiffies加 1。開機時內核讀取 RTC 硬件獲取基準時間再換算成對應的jiffies值減去 1970-01-01 UTC 的時間差。/proc/uptime中的數值就是基于jiffies換算出來的。這個知識點在今年的面試和課程答辯中經常出現(xiàn)為什么 uptime 是浮點數為什么 loadavg 有三個值如果只答「記錄系統(tǒng)啟動時長」就太淺了。能說出jiffies的遞增機制和系統(tǒng)節(jié)拍HZ的配置才算是真正理解了內核時間系統(tǒng)。報告中這部分屬于加分項建議在答辯時主動提。4. 軟中斷通信實驗信號機制與進程終止順序軟中斷通信是這份報告中代碼量最大、也最值得細讀的實驗。實驗要求用信號如 SIGQUIT、SIGUSR1實現(xiàn)父子進程間的通信并觀察終止順序。核心是理解三件事fork()創(chuàng)建子進程后的父子關系、signal()注冊信號處理函數、kill()發(fā)送信號。4.1 實驗一一個父進程創(chuàng)建兩個子進程用 Ctrl\ 終止流程是這樣的父進程創(chuàng)建兩個子進程 P1 和 P2父進程調用signal(SIGQUIT, stop)注冊信號處理函數然后進入waiting()忙等待用戶在終端按Ctrl\發(fā)送 SIGQUIT父進程收到信號后執(zhí)行stop()將wait_mark置 0退出等待隨后向 P1 發(fā)信號 16、向 P2 發(fā)信號 17兩個子進程收到信號后打印結束信息并退出父進程最后wait(0)回收子進程并退出。核心代碼節(jié)選void waiting() { while (wait_mark ! 0); } void stop() { wait_mark 0; } int main() { int p1, p2; while ((p1 fork()) -1); // 創(chuàng)建第一個子進程 sleep(1); if (p1 0) { // 父進程分支 printf(parent process is %d \n, getpid()); while ((p2 fork()) -1); // 創(chuàng)建第二個子進程 if (p2 0) { wait_mark 1; signal(SIGQUIT, stop); waiting(); kill(p1, 16); // 向子進程 P1 發(fā)信號 16 kill(p2, 17); // 向子進程 P2 發(fā)信號 17 wait(0); wait(0); printf(\n parent process %d is killed!\n, getpid()); exit(0); } else { // 子進程 P2 分支 printf(child P2 process %d is created by %d \n, getpid(), getppid()); signal(SIGQUIT, SIG_IGN); wait_mark 1; signal(17, stop); waiting(); lockf(1, 1, 0); printf(\n child P2 process %d is killed by parent %d , getpid(), getppid()); lockf(1, 0, 0); } } else { // 子進程 P1 分支 printf(child P1 process %d is created by %d \n, getpid(), getppid()); signal(SIGQUIT, SIG_IGN); wait_mark 1; signal(16, stop); waiting(); lockf(1, 1, 0); printf(\n child P1 process %d is killed by parent %d , getpid(), getppid()); lockf(1, 0, 0); } return 0; }代碼的設計說明fork()返回 0 表示當前是子進程返回大于 0 的值是子進程 PID對父進程而言。父進程先通過fork()創(chuàng)建 P1sleep(1)確保 P1 完成初始化再創(chuàng)建 P2。為什么 P1 和 P2 要用不同的信號編號16 和 17因為這樣每個子進程只響應屬于自己的信號避免歧義這對應了「軟中斷信號用于進程間通信」的實驗目的。lockf(1, 1, 0)的作用是加鎖標準輸出防止兩個子進程同時往終端打印時輸出交錯這在并發(fā)編程里是一個典型問題——不加鎖printf 的輸出可能互相穿插。4.2 實驗二父、子、孫三級進程的依次終止實驗二不同于實驗一的「一父兩子」結構而是父進程 → 子進程 → 孫進程的鏈式結構。流程是父進程創(chuàng)建子進程 P1P1 再創(chuàng)建孫進程 P2按 Ctrl\ 后父進程向 P1 發(fā)信號P1 向 P2 發(fā)信號依次終止孫、子、父。進程樹形結構中的終止順序是先殺最深的再殺中間的最后殺根部的。int main() { int p1, p2; while ((p1 fork()) -1); if (p1 0) { // 父進程 wait_mark 1; signal(SIGQUIT, stop); waiting(); kill(p1, 16); wait(0); printf(\n parent process is killed \n); exit(0); } else { // 子進程 while ((p1 fork()) -1); // 創(chuàng)建孫進程 if (p1 0) { // 子進程分支 printf(child P1 process %d is created by the parent %d \n, getpid(), getppid()); signal(SIGQUIT, SIG_IGN); wait_mark 1; signal(16, stop); waiting(); kill(p1, 16); wait(0); printf(\n child P1 process %d is killed by parent %d \n, getpid(), getppid()); exit(0); } else if (p1 0) { // 孫進程分支 printf(grandson process %d is created by the parent %d \n, getpid(), getppid()); signal(SIGQUIT, SIG_IGN); wait_mark 1; signal(16, stop); waiting(); printf(\n grandson process %d is killed by parent %d \n, getpid(), getppid()); exit(0); } } return 0; }這段代碼的邏輯層次很清晰但要注意一個隱蔽的問題子進程和孫進程都注冊了信號 16 的處理函數這意味著如果父進程誤發(fā)了兩次信號 16孫進程和子進程可能同時退出破壞層級順序。所以實驗二的運行結果中「先孫再子」的順序依賴代碼執(zhí)行路徑而不是信號優(yōu)先級——父進程是在wait(0)等子進程結束后才 print 自己的結束信息子進程是在wait(0)等孫進程結束后才 print 自己的結束信息這是一個隱式的順序控制。4.3 避坑指南軟中斷實驗中容易翻車的四個問題1. 現(xiàn)象fork()在編譯時報錯提示隱式聲明。原因源文件缺少#include unistd.h部分舊代碼還依賴#include sys/types.h。解決在文件頭部補全頭文件不要省略unistd.h如果用了wait()還要加#include sys/wait.h。2. 現(xiàn)象程序運行后按 Ctrl\ 沒有反應。原因SIGQUIT的信號處理函數沒有正確注冊或者進程處于后臺運行狀態(tài)nohup 方式啟動。解決確認用signal(SIGQUIT, stop)注冊了處理函數而不是SIG_IGN確認程序在前臺運行Ctrl\只對前臺進程的終端有效。3. 現(xiàn)象父子進程的 printf 輸出交叉混亂。原因多個進程同時搶占標準輸出沒有互斥。解決用lockf(1, 1, 0)加鎖標準輸出打印完再lockf(1, 0, 0)解鎖。這個方法比printf前的sleep更可靠。4. 現(xiàn)象父進程 wait(0) 返回后子進程仍未退出。原因kill(p1, 16)發(fā)送信號后子進程可能還在waiting()忙等待中沒有及時被調度。解決在信號處理函數里將wait_mark置 0 是不夠的還要確保子進程收到信號后能退出waiting()循環(huán)——這里的關鍵是signal(16, stop)注冊的處理函數確實被執(zhí)行??梢杂胹leep(1)或waitpid()替代忙等待使時序更穩(wěn)定。5. 進程調度、線程同步、內存管理與系統(tǒng)調用從模擬到真實實現(xiàn)實驗 3 到實驗 6 的內容覆蓋了操作系統(tǒng)的幾個核心主題進程調度策略、線程同步、內存管理和系統(tǒng)調用。報告中記錄了 FCFS 與 RR 調度策略的驗證、線程同步實驗、內存管理的代碼與結果解讀、以及系統(tǒng)調用的具體實現(xiàn)步驟。5.1 進程調度實驗FCFS 與 RR 的工程驗證實驗 3 要求創(chuàng)建工程并驗證 FCFS先來先服務和 RR時間片輪轉策略。FCFS 的核心指標是等待時間先到達的進程先執(zhí)行短作業(yè)可能排隊很久RR 的核心是時間片每個進程運行一個時間片后輪轉到下一個。設計實驗時需要構造一組進程分別記錄到達時間、服務時間、完成時間和周轉時間并用表格匯總。建議的驗證方式用結構體數組保存進程控制塊模擬調度過程。typedef struct { int pid; // 進程號 int arrive_time; // 到達時間 int service_time; // 服務時間 int finish_time; // 完成時間 int remain_time; // 剩余時間RR 用 } pcb;FCFS 的實現(xiàn)就是遍歷進程數組按到達時間排序后依次執(zhí)行RR 的實現(xiàn)需要維護一個就緒隊列每次執(zhí)行一個時間片如 2 個單位時間片結束后判斷剩余時間是否為 0。報告中的實驗結論是FCFS 的平均周轉時間通常高于 RR但 RR 的響應時間更短如果進程的服務時間差異很大RR 的時間片要選得合理太小則上下文切換開銷大太大則退化為 FCFS。答辯時把這個權衡講清楚比背概念有用得多。5.2 線程同步實驗鎖、信號量與條件變量實驗 4 的線程同步實驗報告沒有詳細列出代碼但通常的做法是經典的「生產者-消費者」或「讀者-寫者」模型。用pthread_mutex_t保護共享資源用pthread_cond_t實現(xiàn)條件等待#include pthread.h pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond PTHREAD_COND_INITIALIZER; int buffer[10]; int count 0; void *producer(void *arg) { pthread_mutex_lock(mutex); while (count 10) { pthread_cond_wait(cond, mutex); } buffer[count] 1; pthread_cond_signal(cond); pthread_mutex_unlock(mutex); return NULL; }這個代碼的兩個關鍵點pthread_cond_wait必須放在 while 循環(huán)里而不是 if 里——因為可能存在虛假喚醒pthread_cond_signal和pthread_mutex_unlock的順序會影響喚醒后線程的調度效率。如果忘記解鎖或者忘記加鎖程序可能死鎖或數據競爭這類問題用valgrind --toolhelgrind可以檢測但最好的辦法是寫代碼時就把「加鎖-操作-解鎖」的邊界畫清楚。5.3 內存管理實驗頁面置換與缺頁統(tǒng)計實驗 5 的內容是內存管理重點是頁面置換算法。報告給出了實驗結果展示和結果解讀但沒有把代碼全部貼出來。常見的設計是模擬 FIFO、LRU、OPT 三種算法統(tǒng)計各自的缺頁次數。報告中實驗 7 的「系統(tǒng)缺頁次數統(tǒng)計」實際就是在真實系統(tǒng)中觀測缺頁行為通過讀取/proc/vmstat中的pgfault字段來統(tǒng)計缺頁次數或者通過ps -o minflt,majflt查看某個進程的缺頁計數。一個實際可用的思路在 C 程序中分配一大塊內存并循環(huán)訪問在訪問前后分別讀取/proc/vmstat的缺頁計數值差值就是觸發(fā)的新缺頁數#include stdio.h #include stdlib.h #include string.h int main() { unsigned long before, after; FILE *fp; int size 100 * 1024 * 1024; // 100MB char *buf malloc(size); fp fopen(/proc/vmstat, r); // 讀取 pgfault 字段存入 before fclose(fp); memset(buf, 1, size); // 觸發(fā)大量缺頁 fp fopen(/proc/vmstat, r); // 讀取 pgfault 字段存入 after fclose(fp); printf(page faults: %lu\n, after - before); free(buf); return 0; }這里的設計說明memset一個 100MB 的緩沖區(qū)如果系統(tǒng)內存緊張會產生大量主缺頁major fault和次缺頁minor fault。實際操作中建議把before和after的讀取封裝成函數避免代碼重復。5.4 系統(tǒng)調用實驗從用戶態(tài)到內核態(tài)的通道實驗 6 的系統(tǒng)調用實驗報告中的核心是編寫用戶態(tài)程序調用syscall或直接調用open、read、write等 API運行后觀察程序的系統(tǒng)調用軌跡。通常的做法是用strace追蹤strace -c ./test_program會統(tǒng)計每個系統(tǒng)調用的次數、耗時和錯誤數。這在實驗報告中是加分項因為能直觀展示read、write、mmap、futex這些系統(tǒng)調用的真實行為。如果課程要求新增一個系統(tǒng)調用部分高校的實驗內容是給內核添加一個系統(tǒng)調用通常的做法是修改系統(tǒng)調用號表、添加內核函數實現(xiàn)、重新編譯內核。這個操作復雜且危險可能導致系統(tǒng)起不來建議在虛擬機里做并做好快照。報告中的實驗 6 沒有走這么深停留在用戶態(tài)調用層面但如果你能在答辯中說出strace的結果含義就已經超過了大多數同學。6. 報告驗收與答辯如何把 95 分實驗講成自己的東西實驗報告的驗收通常分兩部分文檔審查和現(xiàn)場答辯。文檔審查看的是格式規(guī)范、實驗步驟完整性、實驗結果是否有截圖、代碼是否可復現(xiàn)現(xiàn)場答辯看的是隨機抽查的某個實驗的核心機制。根據這份報告的經驗我把驗收作答的要點整理成一張核對表檢查項合格標準常見扣分點實驗目的明確列出本次實驗要理解什么概念從網上抄一段「了解 Linux」就算完實驗步驟能復現(xiàn)命令和代碼對應只有代碼沒有運行步驟實驗結果截圖或輸出且有解讀貼了圖但沒解釋為什么是這個輸出實驗總結記錄遇到的問題和解決辦法直接跳過或抄教材原文答辯時最容易翻車的兩個問題一是「這個程序如果去掉 sleep(1) 會怎樣」二是「為什么用信號 16 而不是 SIGUSR1」。前者考察對并發(fā)時序的理解——去掉sleep(1)后父進程可能在子進程完成初始化前就fork()第二個子進程導致進程關系錯亂后者考察對 Linux 信號編號的掌握——SIGUSR1 在部分架構上是信號 10SIGUSR2 是信號 12但代碼中選擇 16 和 17 屬于自定義信號需要確保內核支持。這個問題的回答思路是信號編號對應關系依賴平臺課程實驗中固定使用 16 和 17 是為了簡單明確但實際工程中建議使用SIGUSR1并配合sigaction()注冊這樣可讀性和可移植性都更好。此外答辯時有一個實用技巧提前準備 3 個「為什么」。比如軟中斷實驗的代碼里為什么要用wait_mark忙等待而不直接pause()因為忙等待不需要額外引入信號邏輯直觀但 CPU 空轉如果換成pause()可以讓出 CPU但需要處理「信號到達時進程處于可中斷睡眠」的狀態(tài)轉換。你能主動說出這個權衡說明不是只抄了代碼而是真正理解了設計取舍。提示如果被問到「實驗結果和預期不一致怎么辦」不要慌張。先把實際輸出貼出來指出哪一步和預期不同然后給出排查方向——多數情況是wait()回收順序問題或信號編號沖突。從那以后我每次做完實驗都會強制自己走一遍「去掉一個關鍵代碼再跑一次」的流程。比如軟中斷實驗去掉lockf觀察輸出是否錯亂調度實驗把時間片改成 1觀察周轉時間變化。這樣做的好處是任何「為什么這樣寫」的問題都能用自己的實驗結果來回答而不是背答案。希望幫到你。本文還有配套的精品資源點擊獲取