實驗報告拆解:從Linux信號機制到缺頁統(tǒng)計)
簡介西南交通大學(xué)操作系統(tǒng)實驗報告HXP評分A/95分是面向軟件工程專業(yè)學(xué)生、聚焦Linux環(huán)境與操作系統(tǒng)核心機制的課程實踐資料適合需要參考完整實驗流程或撰寫同類報告的高校學(xué)習(xí)者。報告以兩個主線實驗展開實驗一從Linux界面、目錄結(jié)構(gòu)入手通過終端掌握cd、ls、ps、chmod等Shell命令并利用重定向與管道完成命令組合隨后用vi編輯C源程序經(jīng)gcc編譯、gdb調(diào)試熟悉Linux下的程序開發(fā)與排錯方法。實驗二重點剖析/proc虛擬文件系統(tǒng)通過編程讀取cpuinfo、version、stat、uptime、meminfo、loadavg、interrupts等內(nèi)核文件理解系統(tǒng)狀態(tài)與內(nèi)存管理。后續(xù)實驗還涵蓋軟中斷通信、進程調(diào)度和線程同步完整展示了從基礎(chǔ)操作到內(nèi)核觀測的實踐路徑。壓縮包僅含1個docx文檔共1.48MB報告帶清晰目錄結(jié)構(gòu)便于直接查閱或打印。該資源已有401人學(xué)習(xí)對準備Linux實驗、復(fù)習(xí)進程調(diào)度或需要高分參考模板的同學(xué)具有較高借鑒價值。1. 一份95分的操作系統(tǒng)實驗報告比代碼更重要的是過程記錄做操作系統(tǒng)實驗最怕的不是寫不出代碼而是寫得出來卻說不清楚設(shè)計思路和實驗現(xiàn)象。這份在某高校胡老師班拿到95分的實驗報告覆蓋了Linux環(huán)境與C調(diào)試、軟中斷通信、進程調(diào)度、線程同步、內(nèi)存管理、系統(tǒng)調(diào)用、缺頁統(tǒng)計七個實驗正好是大多數(shù)高校操作系統(tǒng)課的核心實驗矩陣。我拆完這份資源后最直接的感受是它值錢的地方不在那些現(xiàn)成的C代碼而在每份實驗里“怎么描述現(xiàn)象、怎么解釋結(jié)果、怎么踩坑再爬出來”的記錄方式。如果你正在補實驗報告、準備課程設(shè)計素材或者想看看95分的學(xué)生是怎么組織實驗文本的這份報告可以作為一份完整的參照模板。代碼可以重寫但那種對實驗現(xiàn)象的觀察深度直接抄是抄不來的。2. 報告的骨架拆解七個實驗覆蓋了Linux操作系統(tǒng)的哪些核心機制2.1 從目錄結(jié)構(gòu)看課程設(shè)計的完整鏈路這份報告從實驗一到實驗七鏈路設(shè)計有明顯遞進關(guān)系。實驗一和觀察Linux是基礎(chǔ)層解決的是“會不會用Linux”的問題——目錄結(jié)構(gòu)、基本Shell命令、gcc編譯、vim編輯、/proc文件系統(tǒng)的基本讀取實驗二和三進入進程層用軟中斷通信和進程調(diào)度理解“進程是怎么被創(chuàng)建、通信和切換的”實驗四到六延伸到線程同步、內(nèi)存管理和系統(tǒng)調(diào)用屬于并發(fā)與資源管理的范疇最后的缺頁次數(shù)統(tǒng)計實驗則把視角拉到內(nèi)存管理的底層用/proc/stat讀取缺頁信息來驗證理論。這個順序和你自己學(xué)校的實驗安排大概率能對上。如果你們學(xué)校的實驗順序不同也不用擔(dān)心報告的每個實驗是相對獨立的你可以直接挑對應(yīng)的那章來看不必從頭按順序讀。我拆這份報告時是按“能直接用”的標(biāo)準來篩選的——也就是說每個實驗的代碼、編譯命令、實驗結(jié)果描述方式都要能單獨抽出來復(fù)用到你自己的報告里。2.2 評分高的關(guān)鍵在哪個人聲明與問題記錄報告的序言后面緊跟一段“個人聲明”很簡短就說了一件事報告里用到的命令統(tǒng)一用粗體加下劃線標(biāo)注方便閱覽。這個細節(jié)看起來不起眼其實是給評閱老師的第一印象分。95分的報告通常不是每一個實驗都完美但整份文檔的格式一致性、可讀性、以及問題記錄的誠實度拉高了整體評價。報告中每一章結(jié)尾幾乎都有“實驗總結(jié)”部分里面寫的不只是成果還有具體的失敗記錄。比如實驗一里寫了“配置沒有問題但網(wǎng)絡(luò)連接一直失敗”然后記錄了怎么查找博客、修復(fù)網(wǎng)絡(luò)順帶學(xué)習(xí)了三種網(wǎng)絡(luò)模式的區(qū)別。這種寫法比單純寫“我學(xué)會了Linux目錄結(jié)構(gòu)”有價值得多因為老師能從文字里看到真實的動手過程。你在寫自己的報告時建議每章末尾都留出一段“問題與解決”哪怕只寫兩條也比空泛的“通過本次實驗我掌握了”要有說服力。2.3 環(huán)境選型用什么Linux跑這套實驗最省事報告全程在虛擬機里完成涉及的環(huán)境工具包括vim、gcc、gdb、以及遠程傳文件的xftp。從報告里觀察/proc/version的結(jié)果來看實驗環(huán)境是Red Hat系列的Linux發(fā)行版內(nèi)核版本3.10.0。這個內(nèi)核版本對應(yīng)的系統(tǒng)通常自帶gcc 4.8.5對于實驗中的fork、signal、pthread這些接口來說完全夠用。如果你現(xiàn)在要從零復(fù)現(xiàn)這套實驗我建議直接用Ubuntu 20.04以上的發(fā)行版理由有兩點一是gcc版本更新編譯時的警告信息更友好二是apt安裝build-essential一條命令就能補齊工具鏈。當(dāng)然要是你們學(xué)校指定了某個實驗環(huán)境以學(xué)校要求為準。虛擬機的網(wǎng)絡(luò)模式報告里也提到了三種——橋接、NAT、主機模式并做了個人理解式的總結(jié)橋接容易IP沖突、NAT既能訪問外網(wǎng)又不暴露虛擬機、主機模式完全隔離。對于實驗場景NAT是最省心的選擇。提示報告中有一段關(guān)于jiffies的理解——Linux內(nèi)核用全局變量jiffies記錄節(jié)拍時間開機時讀取RTC作為基準這個思路和Windows的滴答計數(shù)是同一類機制。寫報告時能把這類課外延伸寫進去是加分的。3. 軟中斷通信實驗把信號機制落到兩份可復(fù)現(xiàn)的C代碼3.1 實驗要求與信號機制的關(guān)系軟中斷通信實驗是這份報告里代碼價值最高的部分。實驗要求分兩種場景場景一是父進程創(chuàng)建兩個子進程然后通過Ctrl\發(fā)送SIGQUIT給父進程由父進程再向兩個子進程發(fā)送自定義信號終止它們場景二是父進程創(chuàng)建子進程、子進程再創(chuàng)建孫進程信號到達后按孫進程、子進程、父進程的順序依次終止。要理解這個實驗先要分清兩個概念SIGQUIT和信號16/17。SIGQUIT是終端組合鍵Ctrl\產(chǎn)生的信號默認動作是終止進程并生成核心轉(zhuǎn)儲而信號16和17在Linux下沒有標(biāo)準定義屬于用戶自定義信號實驗里用它來模擬父子進程間的“軟中斷”通信。流程就是父進程收到SIGQUIT后調(diào)用kill函數(shù)向子進程發(fā)送16/17信號子進程收到后設(shè)置wait_mark為0退出等待循環(huán)。3.2 第一個場景父進程創(chuàng)建兩個子進程的代碼這是報告里實驗二的第一個核心代碼我重新組織了注釋和說明讓它更便于你直接編譯運行#include stdio.h #include stdlib.h #include signal.h #include unistd.h void waiting(); void stop(); int wait_mark; int main() { int p1, p2; while ((p1 fork()) -1) ; // 創(chuàng)建子進程p1失敗則忙等重試 sleep(1); if (p1 0) // 父進程分支 { printf(parent process is %d \n, getpid()); while ((p2 fork()) -1) ; // 創(chuàng)建子進程p2 if (p2 0) // 仍然是父進程分支 { wait_mark 1; signal(SIGQUIT, stop); // 注冊SIGQUIT處理函數(shù) waiting(); // 等待用戶按下Ctrl\ 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); // 忽略SIGQUIT wait_mark 1; signal(17, stop); // 注冊信號17處理函數(shù) 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); // 忽略SIGQUIT wait_mark 1; signal(16, stop); // 注冊信號16處理函數(shù) waiting(); lockf(1, 1, 0); printf(\n child P1 process %d is killed by parent %d , getpid(), getppid()); lockf(1, 0, 0); } return 0; } void waiting() { while (wait_mark ! 0) ; // 忙等直到wait_mark被信號處理函數(shù)置0 } void stop() { wait_mark 0; }這份代碼的核心邏輯不復(fù)雜但有三個地方需要特別說明。第一是fork后的分支判斷。fork()返回0表示當(dāng)前在子進程中返回正整數(shù)表示當(dāng)前在父進程中返回-1表示創(chuàng)建失敗。代碼里用了while循環(huán)包裹fork目的是防止進程數(shù)達到系統(tǒng)上限時創(chuàng)建失敗忙等重試直到成功。sleep(1)放在第一個fork后面是為了讓子進程有時間完成后續(xù)的signal注冊避免父進程信號發(fā)過去時子進程還沒準備好。第二是signal(SIGQUIT, SIG_IGN)這一段。子進程必須忽略SIGQUIT否則用戶按下Ctrl\時父子進程會一起被系統(tǒng)殺死就看不到實驗要求的“父進程轉(zhuǎn)發(fā)信號終止子進程”這個現(xiàn)象了。父進程負責(zé)接收SIGQUIT子進程只接收來自父進程的信號16或17這是整個實驗的信號流向設(shè)計。第三是lockf的使用。兩個子進程都可能在waiting()退出后調(diào)用printf輸出結(jié)束信息如果不加鎖兩個進程的輸出會交錯在一起終端顯示亂掉。lockf(1, 1, 0)是對標(biāo)準輸出加寫鎖lockf(1, 0, 0)是解鎖保證一個進程的輸出是原子的。3.3 編譯與運行驗證編譯命令很簡單gcc直接編譯即可gcc -o sig_test1 sig_test1.c ./sig_test1程序啟動后會輸出父進程和兩個子進程的PID然后卡住——這是父進程在waiting()里忙等。此時按下Ctrl\終端會顯示^\隨后看到兩個子進程依次打印被終止的信息最后父進程打印退出信息。需要提醒的是代碼里的信號編號16和17在部分新版本Linux上可能已經(jīng)被系統(tǒng)占用。signal(16, stop)在某些架構(gòu)上可能注冊失敗。如果你在運行的發(fā)行版上遇到“這個信號無法使用”的情況可以把16和17換成SIGUSR1和SIGUSR2這是系統(tǒng)保留給用戶程序使用的標(biāo)準信號語義上更規(guī)范。這是我拆這份報告后建議你做的一個改進點。3.4 第二個場景三層進程父、子、孫的終止順序第二段代碼做的是三進程傳遞關(guān)鍵點在于子進程既接收父進程的信號又轉(zhuǎn)發(fā)信號給自己的子進程。流程是父進程收到SIGQUIT后向子進程發(fā)信號16子進程的信號處理函數(shù)執(zhí)行wait_mark0子進程從waiting()退出后再向?qū)O進程發(fā)信號16等孫進程結(jié)束后自己再退出。這樣從用戶視角看按下Ctrl\依次看到孫進程、子進程、父進程的結(jié)束信息。這段代碼我再貼一次核心結(jié)構(gòu)因為很多人會在“誰的wait_mark先被置0”這個問題上繞暈// 父進程分支 wait_mark 1; signal(SIGQUIT, stop); waiting(); kill(p1, 16); // 向子進程發(fā)送信號16 wait(0); // 等待子進程退出 // 子進程分支 signal(SIGQUIT, SIG_IGN); signal(16, stop); waiting(); kill(p1, 16); // 子進程向?qū)O進程發(fā)送信號16 wait(0); // 等待孫進程退出 // 孫進程分支 signal(SIGQUIT, SIG_IGN); signal(16, stop); waiting(); // 打印結(jié)束信息退出這段代碼里最容易忽略的是每個進程在waiting()退出后的執(zhí)行順序。子進程的kill(p1, 16)調(diào)用和孫進程的waiting()退出之間存在一個時間窗。因為孫進程在收到信號16后才會把wait_mark置0所以子進程kill之后必須調(diào)用wait(0)阻塞等待孫進程結(jié)束。如果不加wait(0)子進程會立刻繼續(xù)執(zhí)行printf和exit終端上看到的順序就可能變成“子進程先結(jié)束、孫進程再結(jié)束”和實驗要求不符。4. 進程調(diào)度與線程同步從報告看驗證環(huán)節(jié)的設(shè)計思路4.1 調(diào)度實驗的驗證思路FCFS與RR不能只看代碼報告里的實驗三是進程調(diào)度實驗實驗步驟寫得比較簡略大致是創(chuàng)建工程后驗證FCFS先來先服務(wù)與RR時間片輪轉(zhuǎn)兩種策略。很多同學(xué)做這個實驗時有一個誤區(qū)代碼跑出結(jié)果就算做完。但從這份95分報告的風(fēng)格來看驗證環(huán)節(jié)比代碼本身更值得花心思。FCFS和RR的區(qū)別要落到你畫的進程執(zhí)行順序圖上。假設(shè)有三個進程到達時間分別是0、1、2服務(wù)時間分別是4、3、2FCFS下進程按到達順序執(zhí)行總完成時間依次是4、7、9RR下如果時間片為2進程輪流占用CPU執(zhí)行順序會完全不一樣。報告里沒有貼具體的進程數(shù)據(jù)但你在復(fù)現(xiàn)時應(yīng)該自己構(gòu)造好這樣一組數(shù)據(jù)并在報告里畫出調(diào)度時序圖。調(diào)度實驗的代碼通常是模擬實現(xiàn)和內(nèi)核里的真實調(diào)度器沒有直接關(guān)系。報告里的做法是在用戶態(tài)用C語言實現(xiàn)一個調(diào)度器的模擬框架定義PCB結(jié)構(gòu)體、就緒隊列、時間片計數(shù)器然后按不同策略跑同一組進程數(shù)據(jù)輸出完成時間、周轉(zhuǎn)時間、帶權(quán)周轉(zhuǎn)時間。這個模擬過程的價值在于你能直觀看到RR策略下短進程沒有被長時間阻塞、但上下文切換次數(shù)上升了這個典型 trade-off。4.2 線程同步實驗鎖定與信號量的標(biāo)準寫法實驗四是線程同步實驗按報告的目錄結(jié)構(gòu)看實驗內(nèi)容圍繞線程的同步互斥展開。線程同步和進程同步最大的區(qū)別在于線程共享同一地址空間不需要像進程那樣通過信號或管道來傳遞數(shù)據(jù)直接用共享變量配合鎖、信號量或條件變量就能完成。這里有一個常見的翻車點用while循環(huán)包裹wait操作是推薦寫法但很多人會隨手寫成if。兩者的區(qū)別是if只在被喚醒時檢查一次條件而while會在每次被喚醒后重新檢查條件是否仍然滿足。多線程環(huán)境下可能發(fā)生虛假喚醒或者多個消費者被同時喚醒后只有一個能拿到資源如果沒有用while重新檢查條件就會產(chǎn)生數(shù)據(jù)不一致。這個級別的分析寫進實驗報告是明顯加分的內(nèi)容。4.3 內(nèi)存管理與缺頁統(tǒng)計實驗/proc是觀測窗口實驗五是內(nèi)存管理實驗通過/proc文件系統(tǒng)觀察系統(tǒng)的meminfo、loadavg、interrupts、filesystem等信息實驗七是缺頁次數(shù)統(tǒng)計核心是讀取/proc/stat里的缺頁相關(guān)字段。這兩個實驗的關(guān)鍵是理解/proc下每個文件對應(yīng)的內(nèi)核數(shù)據(jù)來源。比如/proc/meminfo里的MemTotal、MemFree、Buffers、Cached字段直接反映了系統(tǒng)物理內(nèi)存的使用分布/proc/stat里的pgfault和pgmajfault字段前者是普通缺頁次數(shù)包含從頁面緩存中恢復(fù)的后者是主要缺頁次數(shù)需要磁盤I/O兩者之間的差距能反映內(nèi)存回收和頁面緩存的效率。讀取這些數(shù)據(jù)的C程序本質(zhì)上就是打開文件、讀字符串、按空格解析、打印輸出。報告里強調(diào)“不能粘貼運行結(jié)果”意思是必須用自己的代碼去讀取然后對數(shù)值做解釋。你要是自己寫的話一個典型的解析流程是#include stdio.h #include stdlib.h #include string.h int main() { FILE *fp; char line[256]; unsigned long pgfault 0, pgmajfault 0; fp fopen(/proc/stat, r); if (!fp) { perror(open /proc/stat); return -1; } while (fgets(line, sizeof(line), fp)) { if (strncmp(line, pgfault, 7) 0) { sscanf(line, pgfault %lu, pgfault); } else if (strncmp(line, pgmajfault, 10) 0) { sscanf(line, pgmajfault %lu, pgmajfault); } } fclose(fp); printf(pgfault: %lu\n, pgfault); printf(pgmajfault:%lu\n, pgmajfault); return 0; }這段代碼做的事情很直接打開/proc/stat文件逐行讀取用strncmp匹配以pgfault和pgmajfault開頭的行sscanf提取數(shù)值最終打印。實測時發(fā)現(xiàn)pgfault的數(shù)值增長很快而pgmajfault的增長相對緩慢這就是頁面緩存命中率的體現(xiàn)——大部分缺頁都能從用戶空間的頁面緩存里恢復(fù)不需要真正發(fā)起磁盤I/O。注意/proc/stat里的pgfault和pgmajfault是系統(tǒng)累計值不是某個進程的缺頁數(shù)。想要統(tǒng)計指定進程的缺頁次數(shù)需要讀取/proc/[pid]/stat里的minflt和majflt字段這個區(qū)別寫報告時容易搞混。報告中實驗七是按“系統(tǒng)缺頁次數(shù)”做的所以用/proc/stat是對的。5. 避坑指南六個能讓你重寫報告的常見問題5.1 fork代碼從Windows復(fù)制到Linux后無法編譯現(xiàn)象在Windows的IDE里寫好C代碼復(fù)制進Linux后用gcc編譯報錯fork undeclared或signal not declared。原因Windows的C標(biāo)準庫沒有UNIX的POSIX接口fork、signal這些函數(shù)是Linux系統(tǒng)調(diào)用Windows環(huán)境下IDE的庫根本不包含它們代碼里雖然寫了#include unistd.h和#include signal.h但編譯器找不到對應(yīng)實現(xiàn)。解決直接在Linux虛擬機里用vim編寫或粘貼代碼然后在Linux的終端里完成編譯。如果你實在習(xí)慣用Windows編輯器寫代碼用xftp之類的工具傳進虛擬機再編譯但不要把Windows下的編譯錯誤當(dāng)成Linux下的行為來排查。報告中提到的xftp傳文件方式對于傳大量小文件確實比反復(fù)復(fù)制粘貼省事。5.2 按下Ctrl\沒有反應(yīng)程序繼續(xù)卡住現(xiàn)象程序運行后按Ctrl\終端沒有任何反應(yīng)進程不退出。原因兩種情況最常見。第一種是子進程沒有忽略SIGQUIT導(dǎo)致Ctrl\直接終止了所有收到該信號的進程你還沒看到輸出終端就回到Shell了第二種是父進程注冊了SIGQUIT處理函數(shù)但waiting()里的wait_mark變量被編譯器優(yōu)化了。解決確認子進程分支里調(diào)用了signal(SIGQUIT, SIG_IGN)父進程分支里調(diào)用了signal(SIGQUIT, stop)。如果確認沒錯把wait_mark聲明為volatile int防止編譯器把它優(yōu)化到寄存器里導(dǎo)致信號處理函數(shù)改了值、主循環(huán)卻感知不到。這是比較隱蔽的坑通常在開啟-O2優(yōu)化后暴露。5.3 兩個子進程的輸出信息混在一起現(xiàn)象實驗一里P1和P2的結(jié)束信息同時出現(xiàn)在終端字符交錯在一起無法辨認。原因兩個子進程同時從waiting()退出同時調(diào)用printf輸出到標(biāo)準輸出。printf雖然有行緩沖但多個進程同時操作同一文件描述符沒有原子性。解決用lockf對標(biāo)準輸出加文件鎖。lockf(1, 1, 0)對文件描述符1stdout加寫鎖lockf(1, 0, 0)解鎖。同一時刻只有一個進程能持有鎖輸出就變成原子操作了。這也是報告中代碼里lockf出現(xiàn)的原因在復(fù)現(xiàn)或講解時不要省略它。5.4 gdb調(diào)試時斷點不生效現(xiàn)象用gdb設(shè)置斷點后運行程序斷點沒有被命中程序直接在終端跑完了。原因最常見的情況是編譯時沒有加-g參數(shù)。gdb依賴調(diào)試符號表來關(guān)聯(lián)源代碼行號和機器指令地址沒有調(diào)試信息斷點自然無法設(shè)置。解決編譯時使用gcc -g -o test test.c。如果還需要更多調(diào)試信息可以用gcc -g3 -O0-g3包含宏定義信息-O0關(guān)閉優(yōu)化避免調(diào)試時變量被優(yōu)化掉。另外如果你用的是Code::Blocks這類IDE調(diào)試要確認項目的編譯選項里開啟了“生成調(diào)試信息”這一項。5.5 缺頁統(tǒng)計實驗結(jié)果不穩(wěn)定現(xiàn)象連續(xù)兩次運行缺頁統(tǒng)計程序打印出的數(shù)值差異巨大甚至第二次運行比第一次運行數(shù)值還小看起來不合理。原因/proc/stat里展示的是從系統(tǒng)啟動以來的累計值每次讀取都是一個更大的數(shù)字不會變小。但如果你讀取的是某個特定進程的/proc/[pid]/stat字段進程每次運行的minflt和majflt都會不同因為它們?nèi)Q于程序運行時的訪問模式、是否有文件I/O、系統(tǒng)的頁面緩存命中率等因素。差異大是正常的關(guān)鍵在你怎么解讀。解決報告里寫結(jié)果時不要只寫一個時間點的靜態(tài)值而是連續(xù)采樣幾次記錄數(shù)據(jù)的變化趨勢再結(jié)合當(dāng)前系統(tǒng)的負載情況做解釋。比如連續(xù)讀取5秒內(nèi)pgfault的增長量可以估算系統(tǒng)每秒的缺頁頻率這個頻率如果在穩(wěn)定區(qū)間波動就能證明程序的觀測方法是有效的。5.6 報告里貼了大量代碼卻沒有結(jié)果解讀現(xiàn)象實驗報告里代碼占了整頁實驗結(jié)果只貼了一張截圖沒有任何文字說明。原因這是最常見的丟分點。老師評實驗報告不是看你會不會復(fù)制代碼而是看你能不能解釋代碼行為和實驗現(xiàn)象。一張沒頭沒尾的截圖換誰都看不懂你想表達什么。解決至少寫三部分內(nèi)容。第一運行環(huán)境的簡要說明包括Linux版本、內(nèi)核版本第二輸入數(shù)據(jù)是什么比如調(diào)度實驗中三個進程的到達時間和服務(wù)時間第三輸出結(jié)果怎么解讀比如為什么P2在RR策略下比FCFS策略下完成得更早這個現(xiàn)象反映了什么調(diào)度特性。這也是這份95分報告里做得最好的地方——每個實驗結(jié)果后都有一小段“結(jié)果分析”單獨解釋圖中現(xiàn)象發(fā)生的原因。6. 驗證方法論把一份課堂報告變成你自己的能力憑證拿到這份資源后怎么判斷你已經(jīng)“吃透”了而不是“抄完”了我習(xí)慣用一組快速驗證來檢測你可以照著過一遍。第一項驗證是軟中斷實驗的改造能力。原代碼用信號16和17做通信你把它改成用SIGUSR1和SIGUSR2重新編譯運行確認三種進程的終止順序和原來一致。能改得動說明你理解了信號編號只是觸發(fā)標(biāo)識真正決定行為的是每個進程注冊的處理函數(shù)。第二項驗證是/proc讀取程序的擴展能力。寫一個與報告功能等價但代碼結(jié)構(gòu)完全不同的C程序比如用一次性fread讀取整個/proc/stat文件再解析而不是fgets逐行掃描。能改用另一種方式實現(xiàn)相同功能說明你對文件I/O和字符串解析有判斷力而不是只會背模板。第三項驗證是敘述視角的轉(zhuǎn)換。把報告里的一段實驗結(jié)果用自己的話重新描述一遍不要看原文。如果寫出來的文字仍然包含以下要素輸入條件、執(zhí)行過程、輸出結(jié)果、原因分析說明你已經(jīng)掌握了實驗報告的基本結(jié)構(gòu)。這個能力比代碼本身更通用后面的課程設(shè)計、畢業(yè)設(shè)計都能復(fù)用。我自己拆這種課程資源時有個習(xí)慣永遠先看失敗記錄和問題記錄再看成功代碼。因為代碼只能說明一個版本的功能而問題記錄能讓你避開未來可能踩的同一個坑。這份報告里記錄的網(wǎng)絡(luò)連接問題、Windows與Linux環(huán)境差異、jiffies概念的理解恰恰是實際操作中真正會浪費半天時間的點。從那以后我每次拿到類似的課程資源都強制自己先摘出“遇到的問題”那一頁再決定要不要花時間看代碼。希望這份拆解也幫到你至少可以少走一段彎路。本文還有配套的精品資源點擊獲取