# 作業系統筆記 ## CH 5.8 演算法的評估 ### 定量模式 平均等待時間 ### 佇列模式 李特氏公式:$n$ = $λ$ * $W$ $n$ : 平均佇列長度 $λ$ : 新行程平均到達比率 $W$ : 在佇列平均等待時間 離開佇列的行程數量必須等於到達行程的數量,才能達成系統穩定。 ### 模擬 收集資料指出演算法的效能,更精確。 ### 實做 高風險、高代價,環境會改變。 --- ## CH 6 同步 ### 背景 1. 行程並行執行 2. 但並行的行程如果同時存取相同資料,會造成資料不一致,稱為**競爭情況**(race condition)。 ### 臨界區間問題 每個行程含有一段區間稱為**臨界區間**(critical section)的程式碼,讓行程可以改變共用變數、更新表格等工作。 ![](https://i.imgur.com/dSd06s7.png) 解決臨界區間必須滿足三項條件: 1. **互斥** 有行程正在臨界區間內執行,其他行程不可以在其臨界區間內執行。 2. **進行** 如果有行程正等待進入臨界區,且目前臨界區中沒有行程在執行,則等待行程之ㄧ必須能在有限時間內進入臨界區。 3. **限制性的等待** 在一個行程要求進入臨界區間,而此要求尚未答應前,允許其他行程進入其臨界區內的次數有限制。 ### Peterson 解決方案 1. **兩個行程**的解答。 2. 行程共用兩個變數:`int turn` 和 `boolean flag[2]` `turn` : 表示輪到誰執行。 `flag[]`:表示是否準備進入其臨界區間。 ### 同步之硬體 以**硬體方式**解決,藉由 `鎖(lock)` 保護臨界區間。 - test and set 只有滿足互斥性 ```c= boolean test_and_set(boolean *target){ boolean rv = *target; *target = true; return rv; } ``` ```c= do { while (test_and_set(&lock)); /* do nothing */ /* critical section */ lock = false; /* remainder section */ } while (true); ``` - compare and swap 只有滿足互斥性 ```c= int compare_and_swap(int *value, int expected, int new_value){ int temp = *value; if (*value == expected){ *value = new_value; } return temp; } ``` ```c= do { while(compare_and_swap(&lock, 0, 1) != 0); /* do nothing */ /* critical section */ lock = 0; /* remainder section */ } while (true); ```` ### 互斥鎖 解決臨界區間最簡單的是`互斥鎖(mutex lock)`。 為避免競爭狀況,行程在進入臨界區間前必須取得鎖,離開時釋放鎖。 ```c= do { acquire lock; critical section; release lock; remainder section; } while (true); ``` `acquire()` 的定義如下: ```c= acquire() { while(!available); /* busy wait */ available = false; } ``` `release()` 的定義如下: ```c= release() { available = true; } ``` 以上主要缺點是**忙碌等待(busy waiting)** // 也稱為**自旋(spinning)** 缺點:等待鎖釋放的時候,不停地執行空迴圈無法做其他事,直到鎖釋放後才會脫離空迴圈,進入critical section。 優點:當行程等待一個鎖時不需要內容轉換。 ### 號誌 號誌 S 是一個整數變數,除了初值(1),只能藉由 `wait()` 和 `signal()` 存取。 `wait()` 的定義如下: ```c= wait(S) { while (S <= 0); /* busy wait */ S--; } ``` `signal()` 的定義如下: ```c= signal(S) { S++; } ``` 1. 計數號誌 - 值的範圍不受限制 - 能用來對有限數量的例證組成的資源做控制存取 2. 二元號誌 - 數值可以是 0 或 1 - 行為類似互斥鎖,在沒提供互斥鎖的系統可以取代互斥鎖。 可以用號誌解決同步問題,兩個並行的行程: P<sub>1</sub> 具敘述 S<sub>1</sub> ; P<sub>2</sub> 具敘述 S<sub>2</sub> 假設我們要求 S<sub>2</sub> 必須在 S<sub>1</sub> 完成後才可執行 ```c= synch = 0; // 共同號誌 S1; signal(synch); wait(synch); S2; ``` - 同步號誌初始值 = 0; - 互斥號誌初始值 = 1; --- ## CH 7 死結 ### 死結定義 一組行程陷入互相等待的情況造成行程無法繼續執行,使 CPU 使用率和產出大幅降低。 ### 系統模型 一個系統包含可分配可給競爭行程的有限資源。這些資源可以區分成許多形式或類別,每種形式含有數種相同的**例證**。例如:一個系統有兩個 CPU,則 CPU 這種形式的資源具有兩個例證。 正常模式下,一個行程只能依據以下順序使用資源: 1. **要求**: 必須等候獲得資源。 2. **使用**: 行程能夠在此資源上運作 3. **釋放**: 用完後行程釋放資源。 ### 死結的特性 P = 行程, R = 資源 1. 互斥 一個 R 一次只能被一個 P 使用。 2. 佔用與等候 一個 P 佔用一個 R, 且正在等候其他 P 已佔用的 R。 3. 不可搶先 R 不能被搶先。 4. 循環式等候 R<sub>1</sub> → P<sub>1</sub> → R<sub>2</sub> → P<sub>2</sub> → R<sub>1</sub> → ... 以上**四個條件都成立**,才會產生死結。 ### 資源配置圖 系統資源配置圖 = (V, E) 頂點組合的 V 又可以區分為兩個不同的集合: 1. P = { P<sub>1</sub>, P<sub>2</sub>, ..., P<sub>n</sub> } 行程的集合 2. R = { R<sub>1</sub>, R<sub>2</sub>, ..., R<sub>n</sub> } 資源的集合 有向圖的有向邊代表不同的含意: 1. P<sub>i</sub> → R<sub>j</sub>,**要求邊**,P<sub>i</sub> 已要求 R<sub>j</sub> 中的一個例證,且正等候此資源。 2. R<sub>j</sub> → P<sub>i</sub>,**分配邊**,P<sub>i</sub> 已佔用 R<sub>j</sub> 中的一個例證。 ![](https://i.imgur.com/TOiaepj.png) 1. P1 已經有 R2 的資源,P1 要求 R1 的資源 2. P2 已經有 R1、R2 的資源,P2 要求 R3 的資源 3. P3 已經有 R3 的資源 4. 並沒有形成 cycle,所以沒有 deadlock。 ![](https://i.imgur.com/XggVNvu.png) 1. 承例子 1,只多了一條線,P3 要求 R2 資源 2. 這時候會形成 cycle P1 → R1 → P2 → R3 → P3 → R2 → P1 P2 → R3 → P3 → R2 → P2 3. deadlock ![](https://i.imgur.com/kieIsMk.png) 1. 雖然有 cylce,P1 → R1 → P3 → R2 → P1 2. 但是 P4 可以 release R2 的某一個 instance 3. 那 P3 就可以獲得 P4 release 出的那個 instance 4. 不會 deadlock #### 資源配置圖結論 1. 如果沒有 cycle → 沒有 deadlock 2. 如果有 cycle,不一定有 deadlock - 如果每個 resource 只有一個 instance → deadlock - 如果每個 resource 有多個 instance → 可能 deadlock ### 處理死結 1. 確定系統永遠不會進入死結狀態(deadlocked state) - 預防死結(Prevention) - 避免死結(Avoidance) 2. 允許系統進入死結狀態(deadlocked state),偵測並恢復它 3. 完全無視這些問題,假裝這些問題從來不曾發生過 - 最多作業系統使用的方式,包括 Linux、Windows,它讓程式開發者自己來處理這些問題 ### 預防死結 只要死結四個特性有一不成立,就可以預防死結的發生。 1. 互斥 對可共用資源而言,互斥一定成立;而唯讀檔案因為可以同時讀取相同檔案,所以一定不會產生互斥。因此**不能**藉由排除互斥達到預防死結。 2. 佔用與等候 必須保證一個行程在要求一項資源時,不可以佔用任何其它的資源。兩種可能策略: - 行程在執行前需要求並取得所需資源。 - 行程在未佔用資源的情況下才能要求資源,如要求新資源,必須先釋放所佔用資源。 會造成**資源利用率低**,也可能會有**飢餓現象**。 3. 不可搶先 變成可搶先,行程可以搶奪 waiting process 所持有的資源。 解決:採取類似 "Aging" 技術(將被搶奪的次數,列為提高優先權之依據) 4. 循環式等候 確保循環式等候的條件不成立,對所有的資源型式強迫**安排線性的順序**。 行程要求資源時必須依照數字大小,遞增地提出要求。 優點:保證系統絕不會有死結存在 缺點:資源可用率低、throughput 低 ### 避免死結 #### 安全演算法 如果找不出一組 Safe state 就是在不安全的狀態 ![](https://i.imgur.com/sh8CrUk.png) 1. 剩下 12 - (5 + 2 + 2) = 3,free 2. 剛好 B 需要 2 個就給他,等他做完會釋放 ( 3 + 2 = ) 5 個資源 3. 5 個可以給 A,A 做完釋放 10 個 4. 剩下就可以給 C 用了 5. <B, A, C>是一個 safe state ![](https://i.imgur.com/hKi18xb.png) 1. 剩下 12 - (5 + 2 + 3) = 2,free 2. 剛好 B 需要 2 個就給他,等他做完會釋放 4 個 3. 但是 A、C 都的需要都 > 4 4. 所以會有 deadlock #### 資源配置圖演算法 在資源配置圖中多加入一種邊,稱為 claim edge(宣告邊)做法: 1. 當 Pi 要求 Rj 時 2. 將 claim edge Ri 虛線 → Rj 改為要求邊 Pi → Rj 3. 再將要求邊改為分配邊 Pi ← Rj 4. 查看是否有 cycle 存在,若有就是 unsafe state #### 銀行家演算法 $n$ 為系統中行程數目, $m$ 為資源形式的數量 - `Available` : 長度為 $m$ 的向量,表示可用資源量。 - `Max` : 為 $n * m$ 的矩陣,定義行程最大需求量。 - `Allocation` : 為 $n * m$ 的矩陣,定義每一行程所佔用的資源數量。 - `Need` : 為 $n * m$ 的矩陣,表示每一行程剩餘的需求量。 `Need` = `Max` - `Allocation` 1. 寫出 Need (Max - Allocation) 2. 分配後行程釋放資源,Work = ** Available + Allocation** 3. 重複找資源 ![](https://i.imgur.com/RxEmzZU.png) 1. 寫出 Need ![](https://i.imgur.com/zgqgGwH.png) 2. 把資源給 P1,等到 P1 做完會釋放 (2,0,0),下次資源就有 (3,3,2) + (2,0,0) = (5,3,2) 3. (5,3,2) 的資源可以給 P3,等到 P3 做完會釋放(2,1,1),下次資源就有(5,3,2) + (2,1,1) = (7,4,3) 4. (7,4,3) 的資源可以給 P4,等到 P4 做完會釋放(0,0,2),下次資源就有(7,4,3) + (0,0,2) = (7,4,5) 5. (7,4,5) 的資源可以給 P1,等到 P1 做完會釋放(0,1,0),下次資源就有(7,4,5) + (0,1,0) = (7,5,5) 6. (7,5,5) 的資源可以給 P2,等到 P2 做完會釋放(3,0,2),下次資源就有(10,5,7) 7. <P1, P3, P4, P1, P2> 是一個 safe sequence ### 死結偵測 1. **多久**會產生死結? 2. **多少**行程受死結發生影響? 優點:resource utilization 較高,Throughput 高 缺點:cost 太高 ### 死結恢復 1. 取消所有死結中的行程 2. 一次取消一個行程,直到死結循環消除為止 選擇花費代價最小的行程中止: - 使用最少處理器時間的進程。 - 使用最少輸出工作量的進程。 - 具有最多剩餘時間的進程。 - 分的最少資源進程。 - 具有最小優先級的進程。 ### 資源搶先 若使用搶先處理死結,考慮以下: 1. 選擇犧牲者 2. 回撤 3. 飢餓 --- ## CH 8 記憶體管理 ### 基本硬體 ![](https://i.imgur.com/6PLhekj.png) 1. Base register:程式的起始記憶體地址,稱為**基底暫存器** 2. Limist register:程式所佔記憶體地址大小,稱為**界限暫存器** ### 位址連結 ![](https://i.imgur.com/qhUKXRk.png) 1. 編譯時間(compile time) 編譯時,已知程式在記憶體中的位址,可產生**絕對碼**。 缺點:起始位址有變就必須**重新編譯**。 2. 載入時間(load time) 編譯時不能確定程式在記憶體中的位址,編譯程式必須產生**可重定位碼**,連結動作會被延遲到載入時間才進行。 缺點:起始位址改變需**重新載入**。 3. 執行時間(execution time) 連結的動作被延到執行時間才發生,硬體需要支援 MMU。 ### 邏輯位址和實體位址 1. 邏輯位址 - **CPU** 產生的 - 虛擬位址 2. 實體位址 - 載入到**記憶體**的記憶體位址暫存器所看到的 - 編譯時間和載入時間的位址連結 邏輯位址 = 實體位址 - 執行時間的位址連結 邏輯位址 != 實體位址 ### 記憶體管理單元(MMU) ![](https://i.imgur.com/G5hIsWT.png) 行程所產生的位址被送往記憶體,加上重定位暫存器的值,就會被對映到實體位址。 ### 動態載入 因為程式和資料都須在實體記憶體中執行,所以行程大小會受限於實體記憶體的大小。 要得到較佳的記憶體空間使用率,可採用**動態載入**。 某個常式**需要時才會被載入**,目的是想節省記憶體空間。 ### 動態連結與共用程式庫 當程式被呼叫時才將他載入到記憶體中,也就是**連結被延遲到執行時間**。 節省不必要的連結時間,對於程式庫特別有用,也可以說是**共用程式庫**。 ### 置換 ![](https://i.imgur.com/Gmi6bgy.png) 1. 備份儲存體(backing store) 2. 記憶體不夠用了,優先權(priority)較低的會先被 swap out #### 行動系統的置換 - 不支援置換(用快取記憶體取代硬碟) - IOS 要求 APP 放棄配置記憶體 - Android 會寫入 APP 狀態到快取記憶體 ### 連續記憶體配置 1. fix partition 固定分割,大家都配置一樣的記憶體大小,空間不會釋放 2. Variable partition 配置的記憶體大小符合 process 大小,空間會釋放 ### 動態儲存體配置 1. first-fit 從第一個洞開始找,找到可以塞進去的洞 2. best-fit 檢查所有洞,找出最小的洞,且可以塞進去的洞 3. worst-fit 檢查所有洞,找出最大的洞 ### 斷裂 - 外部斷裂 記憶體的空間被分割成許多小區間,因為行程的載入和移出記憶體,讓區間變成非連續的,不足以分配給其他行程使用,浪費記憶體空間。 - 內部斷裂 記憶體分成固定大小區間,配置給行程使用。但行程沒用到的區間不會釋放給其他行程使用,造成空間的浪費。 ### 百分之五十規則 外部斷裂的問題,配置 N 個區段,有 0.5N 因為斷裂而損失,進而導致記憶體 1/3 沒被利用。 ### 解決外部斷裂 — 聚集 聚集是收集記憶體中零散的可用空間,使其成為一大區間。 不可使用聚集的情況:靜態重定位(絕對程式、可重定位程式) 可以使用聚集的情況:動態可重定位程式 ### 分段 分段表管理位址,每一項都有: 1. 分段基底值 主記憶體中實際開始位址 2. 分段界限值 設定分段的長度 - 優點 - 允許實體位址不連續 - 沒有內部斷裂 - 缺點 - 可能產生**外部斷裂** - 較多記憶體存取時間 * 如何轉換 1. 取得 segement number 之後 2. 利用 segement table 去找到他的 base,就才可以計算起始位置 3. 把 base+offset 才是真的起始位置 4. 加上 limit 就是他在實體位址結束位置 ![](https://i.imgur.com/OTeqiSV.png) ![](https://i.imgur.com/chtLBj4.png) ### 分頁 - 欄(frame) - 將實體位址分成固定大小的區段 - 頁(page) - 邏輯位址分成一樣大小的區塊 - page size = frame size - 利用 page table,儲存 page、frame 關係 - 一個 page 對應一個 frame - 在 os 裡面 - 每一個 process 有自己的 page - 優點 - 可以配置不連續實體位址 - 沒有外部斷裂 - 缺點 - 可能產生**內部斷裂** 一頁的大小一般是 2 的次方,範圍可以自每頁 512B ~ 1G 如果邏輯位址空間是 2<sup>m</sup>,而頁的大小是 2<sup>n</sup> 位元組, 則邏輯位址高階 m - n 個位元代表頁數,低階位元代表頁偏移量,邏輯位址成為: ![](https://i.imgur.com/B9cw4ua.png) 問題: Given a computer system with a 52-bit virtual address, 4KB pages, and 4 bytes per page entry. Suppose that the maximum physical memory size is 512GB, and the system is byte-addressable. Let paging be implemented for the system. What is the number of bits for physical addresses, and what is the maximum number of pages for a process? 答案 a. 39 maximum physical memory size is 512GB 512GB = 2^9 * 2^30 = 2^39 => 39 個 bit b. 2^40 virtual address 是 52-bit page-offset 的長度 => page-size 是 4KB = 2^12 bit page-number 的長度 => 52-12 = 40 page 最大數是 2^40 ### 保護 - 分頁表的每一項都會額外加上另外的位元:**有效 — 無效**位元 ![](https://i.imgur.com/6mYrNS3.png) 有效(v)或無效(i)位元 - 分頁表長度暫存器(PTLR)提供分頁表大小 ### 共用分頁 - 程式碼都是**可重進入程式碼**(text code OR pure code) - 可以不同的邏輯位址但都指到相同的實體位址 ![](https://i.imgur.com/ILVbFkp.png) ### 分頁表的結構 1. 階層式分頁 - 將分頁表也分頁 2. 雜湊式分頁 - 位址空間大於 32 位元常用 - H(P) = address 3. 反轉分頁表 題目:邏輯位址 48 位元,實體位址 32 位元,分頁大小 16K, (1) 若採用單層分頁,會有多少分頁項目? (2) 採反轉分頁表,會有多少項目? ANS: (1) 16K = 2<sup>14</sup>,偏移量 14 位,頁號有 34(48 - 14) = 2<sup>34</sup> (2) 2<sup>32</sup> / 2<sup>14</sup> = 2<sup>18</sup> --- ## CH 9 虛擬記憶體管理 使用者邏輯記憶體與實體記憶體間的分隔。 - 比主記憶體大,佔用較少實體記憶體 - 載入和置換較少,加速 CPU 和資源使用率 製作方式: - 需求分頁 - 需求分段 ### 虛擬位址空間 - 行程間可以共享記憶體 - 鬆散的位址空間。 ### 需求分頁 以分頁為基礎,採用 lazy swapper 技巧。即程式執行之初不將全部的分頁載入記憶體,僅載入執行所須的 分頁,如要處理 page fault 問題,由 OS 另行處理。 (多加一個**有效-無效位元**欄位,用以指示分頁是否在記憶體中) - 需要分頁才把他放進記憶體 - 較少 I/O 需求,甚至不用 - 更快的反應 - 較少記憶體需求 - 更多使用者 - Lazy swapper - 需要某一頁才置換那頁 - 以**頁**為單位,不是以 process ![](https://i.imgur.com/v5GBFiG.png) 利用**有效 — 無效位元** 檢查有沒有此分頁 ![](https://i.imgur.com/p6TtSXM.png) 如果是 bit 的值是 i 就叫做 page fault(分頁錯誤) #### 分頁錯誤 當行程要使用不在記憶體中的分頁時,會產生**分頁錯誤**。 處理方式: 1. 檢查分頁在分頁表的有效無效位元欄位,發出 trap 給 os 2. 如果參考... - 是非法的,無效記憶體位址 → 停止 - 有效參考,但未將該頁載入 → 將該頁載入 3. 找到空的欄 4. 將分頁從 disk swap in 進記憶體 5. 重設表,把 validation bit 改為 v 6. 重新啟動指令 ![](https://i.imgur.com/bgBpxH0.png) #### 性能 - 存取時間($ma$) 如果沒有分頁錯誤,有效存取時間 = 記憶體存取時間 - 分頁錯誤機率($p$) $0 ≦ p ≦ 1$ $有效存取時間 = (1-p) * ma + p(分頁錯誤時間)$ 平均分頁錯誤處理時間是 $8ms$ 和記憶體存取時間是 $200ns$,有效存取時間是: $(1-p) * 200ns + p(8ms) = 200 + 7,999,800 * p$ 有效存取時間和**分頁錯誤比率呈正比**。 若希望可以小於百分之十的減緩,則: $200ns * 1.1 = 220$ $220 > 200 + 7,990,800 * p$ $p < 0.0000025$ ### 寫入時複製 copy on write - 父行程和子行程在最初時分享相同的分頁。 - 子行程若修改共用分頁,則此分頁需要複製(只有修改的才要複製) `fork()` vs. `vfork()` - `fork()` 使用 copy-on-write - 父行程會和子行程同時執行,要達到這種效果,CPU 要有 MMU 硬體支援。 - `vfork()` 不使用 copy-on-write - 父子行程除了 stack 其他都共享。 - vfork() 通常用於沒有 MMU 的 OS,此時父行程會被暫停,直到子行程執行了 exec() 或 exit()。 ### 分頁替換 當分頁錯誤發生,且記憶體無可用欄時,則 OS 必須執行分頁替換。 OS 必須選擇一個**犧牲分頁(victim page)**,將其 swap out 到 disk 來空出一個可用欄,再將 lost page swap in 到此欄。 ![](https://i.imgur.com/7neZRqa.png) 分頁替換方法: ![](https://i.imgur.com/FVkoxDc.png) #### 修改位元 如果沒有空白欄可以使用,我們就必須轉移兩頁(一頁進一頁出)。可以藉由**修改位元**(modify bit,或是髒位元)減少負擔。 - 0:沒被修改過 - 1:從硬碟讀入後被修改過 ### 分頁替換演算法 執行需求分頁必須解決兩個主要問題: 1. 欄的配置演算法 2. 分頁替換演算法 評估一個演算法好壞藉由一連串的記憶體參考,並計算分頁錯誤的次數來評估,一連串的記憶體參考稱為**參考串**。 以下演算法使用參考串為: ![](https://i.imgur.com/zwB1aDf.png) ### FIFO 分頁替換 先進先出演算法 ![](https://i.imgur.com/4d1TkLr.png) 當要替換頁時,選擇在記憶體中**最老**的那頁換掉。 以上總共有 15 次分頁錯誤。 ![](https://i.imgur.com/chVDKX4.png) FIFO 會造成**畢雷地異常**,配置欄越多,分頁錯誤比率可能會增加。 ### 最佳分頁替換 把未來最長時間之內不會用到的那一頁替換掉。 ![](https://i.imgur.com/sOYDmpB.png) 此演算法分頁錯誤次數為 9 次,永遠不會遭遇畢雷地異常的問題。 ### LRU 分頁替換 最佳演算法實際上並不可行,所以可以找一種近似最佳演算法的方法:**近來最少使用演算法(LRU)**。 LRU 是選擇**最久未被使用**的那一頁作為替換。 ![](https://i.imgur.com/rsRObac.png) LRU 和最佳替換一樣,都不會遭遇畢雷地異常現象。它們都是屬於**堆疊演算法**。 ![](https://i.imgur.com/RBfWwXf.png) 堆疊演算法,越上面代表近來最常用到。 ### LRU 近似分頁替換 很少電腦提供足夠硬體真正支援 LRU 分頁替換,所以必須採用其他方法代替。許多系統都會提供**參考位元**提供一點幫助。 參考位元代表的是此分頁近來有無被使用過,為 1 代表有使用過, 0 則是無。 1. 額外參考位元演算法 ![](https://i.imgur.com/c8RAztj.png) 2. 第二次機會演算法 ![](https://i.imgur.com/7cN9Uyc.png) 3. 加強第二次機會演算法 ![](https://i.imgur.com/LRoHSMm.png) ### 計數基礎分頁替換 ![](https://i.imgur.com/unkJO8y.png) ### 頁緩衝演算法 在 Disk 或 I/O 方便時先選出犧牲者進入 Page buffer,降低分頁錯誤時額外的處理。 ![](https://i.imgur.com/mpvCE4s.png) ### 欄配置法 ![](https://i.imgur.com/dv91FCy.png) ### 輾轉現象 CPU 排班程式發現 CPU 使用率降低,就會增加多元程式規劃的程度。新行程就會搶其他行程的頁以工作,造成分頁錯誤,導致 CPU 使用率又降低 → 提升多元程式規劃程度 → ... ,造成輾轉現象,行程忙於置換。 若要使 CPU 使用率能增加,就必須**降低**多元程式規劃程度。 ![](https://i.imgur.com/paD2Vk5.png) ![](https://i.imgur.com/cRuHonl.png) ### 工作組模式 ![](https://i.imgur.com/ncTWI0v.png) ![](https://i.imgur.com/wlMHOr5.png) ### 分頁錯誤頻率 ![](https://i.imgur.com/C3AqqAl.png)