作業系統 · 周志遠 10910 期中、11210 期末 Q1、Q6、Q12 · 2026-11-08
這份地圖照故事分章。第 0 章是最簡單的電腦。後面每一章,先寫這台電腦(或前一章的解法)碰到的問題,再寫解決它的模型,最後把從模型推出來的結論寫成問答。
第 0 章
出處:OSC(Silberschatz《Operating System Concepts》10e)§1.2 computer-system organization、§3.1 process concept。
這台電腦什麼都沒有保護:任何程式都能執行任何 instruction,也能讀寫任何 physical 位址。
沒有 mode bit(第 1 章)、沒有 MMU(memory-management unit,第 3、4 章):任何程式都能執行任何 instruction、讀寫任何位址。
出處:OSC §1.2.1 interrupts(圖 1.5 是 Intel 的 event-vector table);RISC-V 規格書(unprivileged ISA)§1.6。
用詞:本地圖的 trap,指硬體把控制權交給 kernel 的 handler 的那個動作。觸發它的原因有三種:interrupt、exception、system call。這和 RISC-V 規格書的用法一樣。OSC 的 trap(也叫 exception)只指 software 產生的 interrupt:錯誤和 system call。所以讀 OSC 時,看到 trap,要對回本地圖的「exception 或 system call」。
| 原因 | 誰觸發 | 例子 | handler 做完,回到哪一條 |
|---|---|---|---|
| interrupt | device,和目前的 instruction 無關 | timer、disk 讀完、鍵盤 | 下一條(目前這條已經執行完) |
| exception | 目前這條 instruction 執行時出了問題 | 除以零、page fault(要用的頁不在 memory,第 5 章)、在 user mode 執行 privileged instruction | 出問題的那一條。page fault 處理完會重新執行它;除以零時,kernel 通常直接結束這個 process |
| system call | user 程式故意執行 trap 指令 | x86-64 的 syscall、RISC-V 的 ecall、32-bit x86 Linux 的 int 0x80 | trap 指令的下一條 |
例:OSC 圖 1.5 是 Intel 的 event-vector table。0 號是除以零,14 號是 page fault,32–255 號給 device 的 interrupt。int 0x80 會故意觸發 128 號。x86-64 的 syscall 指令不查這張表:它直接跳到 kernel 開機時存在一個專用 register 裡的位址。兩種做法效果一樣,目的地都由 kernel 決定。
1.問:interrupt 會在一條 instruction 執行到一半時發生嗎?
答:不會。CPU 每執行完一條 instruction,才檢查 interrupt-request line。
2.問:為什麼 page fault 處理完,要重新執行同一條 instruction,system call 卻回到下一條?
答:page fault 時,那條 instruction 要的資料不在 memory,所以它沒有做完。trap 指令則已經做完了,它要的服務就是 handler 做的事。
3.問:「關 interrupt」關掉的是什麼?
答:CPU 暫時不理 interrupt-request line。exception 和 system call 不受影響,因為它們不是從這條線來的。但 timer interrupt 會被擋住(第 1 章定理 3)。
出處:OSC §1.1。這一節從兩個角度看 OS。從 user 看,OS 讓電腦好用。從系統看,OS 有兩個角色:resource allocator(分配 CPU、memory、device)和 control program(控制 user 程式的執行,避免錯誤和不當的使用)。
| 類 | 問題 | OSC 的對應 |
|---|---|---|
| 保護 | 一個 process 出錯或惡意,不能影響 OS 和別的 process。 | control program |
| 效率 | 硬體和時間固定時,做更多有用的事。CPU 時間、memory 空間、device 都是要分配的資源。 | resource allocator |
| 方便 | 程式不用知道硬體的細節,也不用知道還有別的 process。例:每個 process 的 logical 位址都從 0 開始;同一份 source code,換一台 OS 重新編譯就能跑。 | user 的角度 |
你的原話:「OS 的每個 feature/design 都是爲了解決 naive 的電腦實際上會碰到的問題的。」
第 1 章
在第 0 章的電腦裡,任何程式都能執行任何 instruction。一個壞程式可以關掉 interrupt、直接操作 device、改掉 interrupt vector。它也可以一直不讓出 CPU。
出處:OSC §1.4.2 dual-mode operation、§1.4.3 timer、§2.3 system calls。
1.問:怎麼判斷一條 instruction 要不要是 privileged?
答:想一想:讓 user 隨意執行它,會不會搶走 kernel 的控制權,或繞過 kernel 的檢查、存取別人的資源?會,它就要是 privileged。進 kernel 的入口(trap 指令)本身不是 privileged。
例:設定 timer、關 interrupt 會搶走控制權。清除 memory、修改 device-status table(kernel 記錄每個 device 狀態的表)、存取 IO device 會繞過檢查。讀目前的時間、執行 trap 指令都不是 privileged。
2.問:user 程式切到 kernel mode 以後,能執行任意的 privileged instruction 嗎?
答:不能,因為 user 的 code 不會在 kernel mode 執行。mode bit 變成 0 時,PC 已經在 trap 入口,接下來執行的是 kernel 的 code。user 能執行 trap 指令,但選不了 trap 後跳到哪裡。所以 user 要做 privileged 的事,只能用 system call 請 kernel 做。kernel 先驗證,再做。
你的原話:「本質上是,執行 priviledge mode 的 instructions,是作業系統本身,因此保證不是惡意的 instructions。」
3.問:一個 process 進入無窮迴圈,永遠不發 system call。kernel 怎麼拿回 CPU?
答:靠 timer interrupt。所以關 interrupt 的指令一定要是 privileged。不然一個 process 關掉 interrupt 以後,就能一直占著 CPU,別的 process 永遠跑不到。
4.問:既然要保護,為什麼不把 load/store 也設成 privileged?
答:每次進 kernel 都有成本:切 mode bit、存 PC、驗證參數。load/store 是最常用的 instruction。如果它是 privileged,每條 load/store 都要用 system call 進 kernel,程式會慢很多。這是保護和效率的取捨。memory 的保護改用第 4 章的 page table 來做。
5.問:把 IO 指令都設成 privileged,device 就安全了嗎?
答:不一定。CPU 讀寫 device controller 的 register 有兩種方法。PMIO(port-mapped I/O)用 in/out 指令,這種指令可以設成 privileged。MMIO(memory-mapped I/O:device 的 register 對到一段位址)用普通的 load/store,而定理 4 說 load/store 不能是 privileged。所以 MMIO 要靠第 4 章的 page table 來擋。
第 2 章
在第 0 章的電腦裡,一個 process 跑完,才換下一個。process 等 IO 時(例:讀 disk 約 10 ms),CPU 只能閒著。
出處:OSC §3.1 的 PCB、§3.2 的 context switch、§4.1 thread 的概念、§4.3 multithreading models。
1.問:為什麼建立 user thread 比建立 kernel thread 快?
答:建立 user thread,只是在 thread library 的表裡加一列,不用 system call。建立 kernel thread,要用 system call 進 kernel 建 TCB。
2.問:在 user thread 之間切換,要 context switch 嗎?
答:不用。context switch 是 kernel 換 TCB:先進 kernel,把 PC、registers 存回 TCB,再載入另一個 TCB。切換 user thread,是 thread library 在 user space 換 PC 和 SP,不進 kernel。
3.問:在 many-to-one 下,一條 user thread 發出會 block 的 system call(例:讀 disk)。同一個 process 的其他 user thread 還能跑嗎?
答:不能。它們共用唯一的一條 kernel thread。那條 kernel thread block 以後,scheduler 不會選它,對到它的 user thread 全部一起停。
4.問:one-to-one 為什麼不能開無限多條 thread?
答:每條 user thread 都要一個 TCB 和一個 kernel stack,它們佔 memory 的 kernel 區。所以 thread 的數量有上限。
5.問:many-to-many 的代價是什麼?
答:要決定哪條 user thread 對到哪條 kernel thread,thread library 和 kernel 都要參與。所以實作複雜,執行時的成本也比較高。
6.問:什麼情況下,many-to-one 的 CPU 使用率比 one-to-one 高?
答:單核、CPU-bound、不做 IO 時。不做 IO,many-to-one 就不會 block。剩下的差別只有切換:many-to-one 切換不進 kernel,one-to-one 切換要進 kernel。一定要單核,是因為 many-to-one 只有一條 kernel thread,一次只能用一個 core;多核時,one-to-one 能讓很多條 thread 同時在不同的 core 上執行。
你的原話(many-to-one 的程式搬到多核會不會變快):「實際上同個時間點,還是只有一個 user thread 會被執行。」
第 3 章
在第 0 章的電腦裡,程式裡的位址就是 physical 位址。這帶來三個問題:
出處:OSC §2.5 linkers and loaders、§2.6 why applications are operating-system specific、§9.1.3 address binding。
1.問:POSIX 是什麼?為什麼換一台支援 POSIX 的 OS,程式只要重新編譯,不用重寫?
答:POSIX 是一份 OS 的 API 標準。source code 只依賴 POSIX 的函式名字,這台 OS 的 ABI 到編譯時才定下來。所以換 OS 時,重新編譯就會換成新 OS 的 ABI。
2.問:用 dynamic linking 時,executable 會比用 static linking 時小嗎?
答:會。static linking 把 library 的 code 複製進 executable,dynamic linking 只留 stub。
3.問:用 dynamic linking 時,compiler 要做什麼?OS 在執行時要做什麼?
答:compiler 要放 stub。執行時,OS 要找到 library:library 已經在 memory 裡,就直接用那一份;不在,就先載入 memory,再 link。
4.問:在一台機器上編譯,拿到另一台機器執行。為什麼用 static linking 不會出現「library not found」?
答:static linking 在 build 時,已經把 library 複製進 executable。dynamic linking 要到執行時才找 library。另一台機器沒有那個 library,就找不到。
5.問:系統把 libc 更新到新版。程式要重新編譯,才會用到新版嗎?
答:用 dynamic linking 的不用:下次執行時,dynamic linker 載入的就是新版。用 static linking 的要重新 link,因為舊版已經複製在 executable 裡。
你的原話:「除非是破壞式更新」。新版的 ABI 如果不相容,用 dynamic linking 的程式也要重新編譯。
6.問:什麼情況下,compile-time binding 最好?
答:簡單的系統,不需要動態的 memory 管理(例:共用 page、virtual memory)。這時程式不用搬,compile-time binding 最簡單,執行時也不用轉譯。例:只跑一個固定程式的嵌入式系統。
7.問:dynamic linking 能配合 load-time binding 嗎?
答:不能。library 的位址要到執行時才定下來,但 load-time binding 在載入時就把位址定死了。所以只有 execution-time binding 配合得上。execution-time binding 怎麼做,由第 4 章回答。
第 4 章
出處:OSC §9.3 paging。
1.問:一個 process 為什麼存取不到 kernel、別的 process、MMIO 的 memory?
答:它的 page table 裡,沒有對到那些 frame 的 PTE。MMU 每次存取都查 page table,所以這個 process 發出的任何位址,都到不了那些 frame。page table 放在 kernel 區,改 PTBR 又是 privileged,所以 user 改不了自己的對應。
2.問:paging 怎麼解決 external fragmentation?
答:每一頁都可以放在任何一個空的 frame,不用連續。所以只要空的 frame 數量夠,就放得下。
3.問:為什麼 page size 一定是 2 的次方?
答:MMU 每次存取都要把位址分成 p 和 d。page size 是 2 的次方時,只要取高位和低位的 bits,不用運算。不是 2 的次方,就要做除法和取餘數,而且每次存取都要做,太慢。
你的原話:「如果確保每個 index 和 page 裏面的 bytes 是 1-1 and onto,那麽 page size = 2^s」(s 是 d 的 bits 數)。
4.問:位址的 bits 不變時,page 變大,PTE 的數量和 logical 位址空間會怎麼變?
答:d 在轉譯時不變,所以 page size = 2 的(d 的 bits)次方。page 變大,就是 d 變寬、p 變窄,所以 PTE 變少。總 bits 不變,所以 logical 位址空間不變。
5.問:page 變大,memory 的使用率會怎麼變?
答:下降。每段資料的最後一頁通常用不滿,這塊浪費叫 internal fragmentation。page 越大,浪費越多。
6.問:shared memory 和 message passing,什麼時候要用 system call?資料量大時,哪個比較快?
答:兩張 page table 指到同一個 frame,就是共用。shared memory 在建立時,要用 system call 進 kernel,改兩邊的 page table;之後的讀寫都是普通的 load/store。兩邊要協調讀寫順序時(課本叫 synchronization),也可能要用 system call。message passing 每次 send/recv 都要進 kernel:資料先複製到 kernel buffer,再複製給對方。複製的時間和資料量成正比,所以資料量大時,shared memory 比較快。shared library 在 memory 裡只放一份,也是同樣的道理。
7.問:load-time binding 的系統要支援 virtual memory。一頁被 swap out 以後,再 swap in 時有什麼限制?
答:只能放回原來的 physical 位置,因為 load-time binding 把 physical 位址寫死在指令裡了。execution-time binding 只要改那一個 PTE,這頁就能放到任何 frame。
第 5 章
出處:OSC §9.3.2 hardware support、§10.1–10.2 virtual memory 和 demand paging。「memory 是 disk 的 cache」出自 P&H(Patterson & Hennessy《Computer Organization and Design》)§5.7。
| 路徑 | 發生什麼事 | 時間 |
|---|---|---|
| TLB hit | 查 TLB 得到 f,讀一次 memory | 約 10 + 100 ns |
| TLB miss | 用 PTBR 讀 memory 裡的 PTE,填回 TLB,再讀資料 | 約 10 + 100 + 100 ns |
| page fault | valid bit 是 0。CPU 產生 exception,經 trap 進 kernel 的 page fault handler。handler 從 disk 搬一整頁到空的 frame、更新 PTE,再重新執行那條 instruction | 約 10⁷ ns |
你的原話:「就是期望值嘛」
1.問:page 變大,TLB miss rate 會怎麼變?
答:下降。TLB 的一個 entry 記一整頁的對應,所以 TLB 一次能涵蓋的範圍是「entries 數 × page size」,這叫 TLB reach。page 變大,TLB reach 就變大。程式一段時間內反覆用到的頁(working set,第 7 章),更容易落在 TLB reach 裡。
2.問:page 變大,page fault rate 會怎麼變?
答:下降。一次 page fault 會搬一整頁進來,page 越大,一次帶回的資料越多。程式有 spatial locality(用了一個位址,很快會用附近的位址),所以接下來要用的資料,很可能就在剛搬進來的這一頁。
3.問:page 變大,處理一次 page fault 的時間會怎麼變?
答:變長,因為每次要從 disk 搬更多資料。
4.問:page fault 要多少次存取才發生一次,EAT 才不會變慢超過 10%?
答:先不算 TLB。EAT ≈ 100 ns +(page fault 率)× 10⁷ ns。要讓(page fault 率)× 10⁷ ns < 10 ns,page fault 率就要小於 10⁻⁶,也就是一百萬次存取最多 fault 一次。從這個式子也看得出來:page fault 率減半,和每次 page fault 的時間減半,效果一樣。
5.問:算 EAT 時,一次存取要查幾次 TLB?
答:TLB hit 和 TLB miss 都只查一次:查 TLB →(miss 時讀 PTE)→ 讀資料。page fault 時,handler 做完要重新執行那條 instruction(第 0 章定理 2),所以會從查 TLB 開始再走一次。
6.問:換 process 時,TLB 要怎麼處理?
答:PTBR 要換成新 process 的 page table。TLB 的 entry 如果沒有記 pid(process identifier,每個 process 的編號),舊 process 的 p → f 對新 process 是錯的,所以 TLB 要整個清掉。換 process 以後,前幾次存取都會是 TLB miss。
第 6 章
第 4 章的 page table 是一整張陣列,用 p 當 index。第 p 個 PTE 的位址 = PTBR + p × (PTE 的大小),也就是從陣列取第 p 項。所以:
例:32 bits 位址、4 KB page、PTE 4 B。p 的 bits = 位址的 bits − log₂(page size) = 32 − 12 = 20 bits。所以表有 2²⁰ × 4 B = 4 MB,要 1,024 個連續的 frame。64 bits 位址、PTE 8 B 時,表要 2⁵² × 8 B = 32 PB。
出處:OSC §9.4 structure of the page table。
page table 是 p → f 的對應。四種結構只差在用什麼當 index。index 決定兩件事:表有多大,以及 TLB miss 時要讀幾次 memory。
| 結構 | 用什麼當 index | 存了哪些 | TLB miss 時讀幾次 memory(32 bits/64 bits) |
|---|---|---|---|
| 一整張 | p | 每一個可能的 p | 1/1 |
| hierarchical | p 切成幾段,一段查一層表。第一層叫 outer page table,下一層叫 inner page table | 只有用到的區段才建表 | 2/6 |
| hashed | hash(p),沿著串列比對 p | 只存用到的 p | 約 1/約 1 |
| inverted | 反過來用 f。第 f 個 entry 記 (pid, p) | 每個 frame 一個 entry,全機一張 | 要搜尋整張表 |
共用的設定:page 4 KB;一個 PTE 在 32 bits 時 4 B、64 bits 時 8 B;physical memory 1 GB。例子程式只用位址空間的最底部(code、data、heap)和最頂部(stack),中間都沒用到。真實的 x86-64 只用 48 bits 位址,所以是 4 層。hierarchical 圖的 inner 0、inner 1023,是 outer page table 第 0 個、第 1023 個 PTE 指到的 inner page table。
1.問:page table 太大時,為什麼要用這些結構?
答:一整張 page table 大到要很多個連續的 frame。paging 已經替程式解決過同樣的問題:page 可以散放在任何 frame(第 4 章定理 2)。現在把同一個方法用在 page table 自己身上:把表切成一頁一頁,散放在任何 frame,再用指標(outer page table、hash 串列)找回每一塊。沒用到的區段就不建。
2.問:程式很小、logical 位址空間很大時,為什麼 hashed 通常比 hierarchical 快?
答:hierarchical 的層數由 p 有幾 bits 決定,和程式大小無關。64 bits 要 6 層,所以每次 TLB miss 要讀 6 次 memory。hashed 只存用到的頁,程式小,串列就短,約讀 1 次。
3.問:為什麼 inverted page table 轉譯比較慢?
答:它用 f 當 index,但轉譯時已知的是 p,要找的是 f。index 的方向反了,只能逐一比對 (pid, p)。
4.問:為什麼 inverted page table 比較省 memory?
答:它每個 frame 一個 entry,全機只有一張,大小只由 physical memory 決定。另外兩種是每個 process 一張,大小和 logical 位址空間或用到的頁數有關。
5.問:一整張 page table 和 inverted page table 各有幾個 entry?
答:一整張:每一個可能的 p 一個,共 2^(p 的 bits) 個。inverted:每個 frame 一個,共(physical memory ÷ page size)個。例:32 bits 位址、4 KB page、512 MB memory 時,一整張有 2²⁰ 個,inverted 有 2²⁹ ÷ 2¹² = 2¹⁷ 個。
6.問:inverted page table 能做 shared memory 嗎?
答:很難。一個 frame 只有一個 entry,只能記一組 (pid, p)。兩個 process 共用一個 frame 時,這個 entry 記不下兩組。
第 7 章
第 5 章的 page fault 約要 10⁷ ns,是讀一次 memory 的 10⁵ 倍。發生 page fault 時,如果沒有空的 frame,就要先踢掉一頁。踢錯了,那一頁很快又要搬回來,就多一次 page fault。整台機器也一樣:很多個 process 搶同一批 frames。
出處:OSC §10.4 page replacement、§10.6 thrashing。「排序鍵」是你在 09/25 的說法。
你的原話(這一點是你提出的模型):「有 process[1], process[2], process[3], ... 他們各自有各自的 working_set[1][1], working_set[1][2], ... 然後 OS 用 round robin 的方式來并行執行每個 process。那在 round robin 的過程中我就可以模擬,現在 physical memory 有那幾個 working set,踢掉哪幾個 working set。」「working set 對我來説也是一個連續的、而不是一個階段一個階段的」
1.問:為什麼 LRU 實際上的 page fault rate 不錯?
答:程式有 temporal locality:最近用過的頁,很快又會再用。LRU 留下的正是最近用過的頁。
2.問:為什麼 stack algorithm 不會有 Belady's anomaly(frame 變多,page fault 反而變多)?
答:stack algorithm 的定義是:n 個 frame 裡的頁,永遠是 n + 1 個 frame 裡的頁的子集。所以 n 個 frame 時 hit 的存取,n + 1 個 frame 時也一定 hit,fault 次數不會變多。
你在 09/25 推出:排序鍵不依賴 frame 數時(LRU、OPT),n 個 frame 裡的頁,永遠是 n + 1 個 frame 裡排序鍵的前 n 名,所以是 stack algorithm。FIFO 的排序鍵是搬進來的時間。一頁什麼時候搬進來,要看前面哪些存取 fault 了,而這依賴 frame 數。所以 FIFO 不保證。這是充分條件。排序鍵依賴 frame 數時,只能說「沒有保證」。例:FIFO;或是 LFU(least frequently used:踢掉被存取次數最少的頁),而次數從這一頁搬進來時開始數。
3.問:compiler 能分析程式,為什麼還是算不準 OPT 需要的未來 reference string?
答:reference string 要程式執行了才有,compiler 只看得到 code。有三個原因:
你的原話(第 2 個原因是獨立的理由):「就算 n 是常數,這裡應該也是無法確定 reference string」。
4.問:frames 很少時,怎麼改寫迴圈,讓 page fault 最少?
答:讓每段時間用到的頁(working set)放得進 frames。把資料切成一頁一頁的塊,一塊做完所有的工作,再換下一塊(loop blocking)。這樣每一頁只搬進來一次,fault 次數等於下限:用到的頁數。塊的大小要剛好一頁,程式裡用 sysconf(_SC_PAGESIZE) 問 OS 一頁多大。把幾個迴圈合併成一個(loop fusion),會讓每次迴圈同時用到更多頁;frames 不夠時,反而一直 fault。
你的原話(在考卷上寫的):「對於每一塊 a, 先把 b 前半加過來,結束後再把 b 的後半對應的那一個塊加過來.只是我們不知道 page 多大.」
5.問:一個 process 一直 page fault。把置換演算法換成 OPT,有用嗎?
答:如果分到的 frames 比 working set 少,連 OPT 也會一直 fault。例:3 頁輪流用、只有 2 個 frame 時,LRU 每次存取都 fault,OPT 也約每兩次存取 fault 一次。所以問題不在置換演算法,而在 frames 不夠。這叫 thrashing:process 花在 paging 的時間,比執行的時間還多。
6.問:CPU 使用率很低,負責 paging 的 disk 卻很忙。OS 該增加還是減少 process?
答:減少。這表示 D > m:輪流中的 process,working set 加起來超過了總 frames 數。每個 process 輪到時,都在把被踢掉的頁搬回來,所以 CPU 使用率才低。再加 process,D 只會更大;換更快的 CPU 也沒用。處理方法有三種:
第 8 章
第 2 章的做法是:一條 kernel thread 等 IO 時,CPU 換給別的 kernel thread。所以 ready 的 kernel thread 常常不只一條,但一個 core 一次只跑一條。scheduler 要決定什麼時候換、換給誰。這決定每條 kernel thread 等多久,也決定 CPU 會不會在還有工作時閒著。
一句話:workload 是固定的,scheduler 只決定每條 kernel thread 什麼時候待在 ready。指標量的是這段 ready 的時間怎麼分;演算法決定怎麼分。
章內順序照 OSC 第 5 章:§5.1 → §5.2 → §5.3 → §5.5。
你的原話(A 是你提出的模型):「核心概念感覺是幾個 pool:waiting, ready, running。在 pool 裏面的是 kernel thread。每個 kernel thread 帶有一段 workload time……放到 running queue 代表正在消耗(workload 的最左側) CPU burst time」
| 指標 | 式子 | 只看 W 的什麼 |
|---|---|---|
| wait time | Wi | 平均、最大、p95(95% 的 Wi 不超過的值) |
| turnaround time | Ti = Ci + Wi + Oi | 同上,每條加上固定部分 Ci + Oi |
| throughput | X = n ÷ L | 每條加上固定部分 ai + Ci + Oi 以後的最大值 |
| CPU utilization | U = ΣCi ÷ (m × L) | 同上 |
| response time | 每次 request(進入系統,或一段 IO burst 結束)之後,第一段 ready 的長度 | 第一段落在哪裡 |
你的原話(第 4–6 點從這個問題推出):「既然 model 的底層,kernel thread list 是 [(CPU burst time, wait, IO burst time)...],那麽這些指標應該可以表示成這些數字?」
| 演算法 | priority 怎麼定 | 重挑的時刻 |
|---|---|---|
| FCFS(first-come, first-served) | 先進 ready queue 的高 | ① ④ |
| SJF(shortest-job-first) | 下一段 CPU burst 短的高 | ① ④ |
| SRTF(shortest-remaining-time-first,preemptive 的 SJF) | 剩下的 CPU burst 短的高 | ① ③ ④ |
| RR(round-robin) | 跟 FCFS 一樣;被 preempt 的排到 ready queue 尾端 | ① ② ④ |
| priority 排程 | 任意定 | non-preemptive:① ④;preemptive:① ③ ④ |
表上的時刻,是 core 上有 kernel thread 在 running 時。core 空著時,任何演算法都在有 kernel thread 進入 ready 時就挑。
你的原話(假設 MLQ 每次回到 ready 都重新決定 queue;由此推出 MLQ 是 MLFQ 的特例):「……MLQ 分類的根據會動態變化,不就變成 MLFQ 嗎?」
你的原話:「這裏的 MLQ/MLFQ 是一個架構,但是要優化的目標/每個 queue 的演算法我覺得都沒有定義,都可以自己填寫」
1.問:SJF 在什麼條件下讓平均 wait time 最小?
答:所有 kernel thread 同時在 ready queue,而且知道每條的下一段 CPU burst。排第 j 個的 burst bj 讓後面 n − j 條都等它,wait time 總和 = Σ bj × (n − j),短的放前面最小。不同時到達時,要用 SRTF。
你的原話:「這樣 greedy 是對的,因為swap任何一組逆序都不會更糟。」
2.問:RR 的平均 turnaround time 一定比 FCFS 小嗎?
答:不一定,看 burst 的長度;burst 都一樣長時 RR 比較差。RR 保證的是 response time:ready queue 裡有 r 條時,每條最多等 (r − 1) × time slice 就拿到 CPU。
3.問:什麼情況會 starvation?
答:一條 kernel thread 的 priority 可能永遠比別人低時。例:SJF 在短的一直進來時;MLQ 用固定 priority 時的低 queue。non-preemptive 時,一段永遠不結束的 CPU burst 也會讓其他的 starvation。解法是 aging。
你的原話:「Non-preemptive 可以永遠占據 running。」
4.問:non-preemptive 的 scheduler 在 IO 做完時會重挑嗎?
答:core 上有 kernel thread 在 running 時,不會:IO 做完是時刻 ③,non-preemptive 不換掉 running 的 kernel thread。core 空著時,才會挑剛進 ready 的那一條。
5.問:為什麼 IO-bound 的 kernel thread 常拿到高 priority?
答:CPU burst 短,先跑它近似 SJF,平均 wait time 小;它很快進 waiting,IO 和別人的 CPU burst 同時進行,L 變小。相反的情況是 convoy effect(OSC §5.3.1):FCFS 時一條 CPU-bound 在 running,IO-bound 的全在 ready queue 等,device 閒著。
6.問:MLFQ 為什麼常比 MLQ 好?
答:MLQ 是 MLFQ 的特例,所以參數選得好的 MLFQ 至少一樣好(不計排程本身的成本)。kernel thread 的表現跟類型不一致,或隨時間改變時,MLFQ 比較好。
7.問:在 NUMA 上,CPU affinity 為什麼讓程式變快?
答:NUMA-aware 的 OS 把 kernel thread 的 memory 配在它所在 CPU 的 local memory;affinity 讓它留在這個 CPU,存取都是 local,cache 也不用重新填。