# 2025q1 Homework1 (ideas) contributed by < `Andrushika` > {%hackmd NrmQUGbRQWemgwPfhzXj6g %} ## 還政於民的 sched_ext 及機器學習如何幫助 CPU 排程 - `vax-r` ### SCX * Scheduler extension and tools * 可以讓使用者實作模擬 kernel thread 排程器、可以動態載入 * 透過 eBPF 載入運行 * 在 linux kernel 中運行的小型程式 * 不修改 kernel 的狀況下從 user space 載入擴充功能 * 可以攔截、觀察、修改系統行為 * e.g. 丟棄封包、監控流量 * 優點:加速排程策略的實驗速度、安全(不需修改 kernel,寫錯不會 crash) ### 引入 Machine Learning #### 目標 * 用 Machine Learning 協助 task migration 的決策 (task migration: 一個 task 是否應該被放到另一個 CPU 上運行) #### 相關工具 * kprobe: 監測 kernel function 執行狀況、累積訓練資料 * stress-ng: 創造 task 負載 #### 訓練資料的累積 * 製造出 CPU imabalance - 大量混雜 I/O bound task 和 CPU bound task * 評估指標 * kernel compilation time (main metric) * number of task migrations * runq length difference ### 我的反思 * 除了上述提到的 metric 外,還有什麼可以用來衡量 CPU 排程的表現? * cache hit/miss rate - 特別是在 task migration 議題上,頻繁更換執行的 CPU 可能不是好事 * I/O waiting time - 實驗時若混雜了大量 CPU bound 和 I/O bound task,那麼評估時是否兩種 task 的 metric 是否都應該考慮,而非只專注降低 CPU bound task 的 turn around time? * 引入 machine learning 改善排程策略,或許也要考慮 model 本身會不會「太肥」;否則就算達成了完美的排程策略,model 本身卻吃掉了絕大部分的效能 * 是否可能引入 reinforcement learning?(動態學習、調整排程策略) * 任何排程策略都免不了面臨不同硬體配置的問題,不同 core 數、是否有 GPU 等 * 使用者的用電腦習慣不同,可以觀察使用者的常用 task,動態改變優先級 * 該學長成為了 SCX top 10 contributor,仍自謙僅做了 refactor 和效能改進;我覺得開源的世界裡任何貢獻都是重要的 * 老師的名言:路見不平,拿 patch 來填 ## 從 CFS, EAS, 到 EEVDF - `Kuanch`, `devarajabc` ### 觀察 CPU 行為的方法 * 使用 QEMU 模擬 Linux 系統 * QEMU 可用來模擬不同 CPU 架構的開源工具,搭配 KVM 可以做為 Hypervisor * 使用 GDB 觀察核心行為 * 可以用 breakpoint 讓 kernel 停在某個狀態,更容易了解其行為 ### CFS (Completely Fair Scheduler) * 較關注 Fairness,依比例公平分配資源 * 最小 vruntime 者會優先得到 CPU 資源 * 多個 task 有相同 vruntime 時,採用 round robin * 使用紅黑樹進行排列 * 可能會造成 starving * 如果玩原神,可能會掉幀 (?) #### virtual runtime $$vruntime = delta\_exec \times \frac{weight\_nice\_0}{task\_weight}$$ * `delta_exec`:實際運行時間 * `weight_nice_0`:基準權重,`nice = 0` 時的權重 `nice` (-20~19) 決定優先級,`nice` 越小,權重越大 * `task_weight`:process 的權重 $$task\_weight =1024 \times 2^{\frac{nice}{5}}$$ ### EEVDF (Earliest eligible virtual deadline first scheduling) * 相較於 CFS,加上了急迫性考量 (deadline),較小的 latency * 有最小 daedline 的 task 會優先得到 CPU 資源 * 加入了 latency-nice 機制 * nice 表示 task 對 CPU 的數量需求 * latency-nice 表達 task 的頻率需求 ### EAS (Energy Aware Scheduling) * Linux Kernel 很多 benchmark 主要考量伺服器應用,而不貼近終端應用 * 終端設備需要考量功耗 * **並非排程器的一種**;可以掛在 EEVDF, CFS 上,讓那些排程策略以其擁有評判效率的機制 * 設備上會同時有大核、小核,應該要依據情況選擇功率最佳的核心去執行 ### 我的反思 一開始的疑問是,為何 `task_weight` 的計算公式是長這樣,還需要用到指數計算?如果改成線性函數,會是甚麼樣子? $$task\_weight = 1024 - (nice \times k)$$ 我的猜測是,若使用線性函數,則不同的 nice 值所代表的權重可能會差異過大;所以使用指數函數,讓 nice 值去影響相對比例,而非固定的增加或減少。 (依據 CFS 的 `task_weight` 公式,每增加/減少 5 nice 值,權重會相差一半) ## 開發具體而微的 Linux 檔案系統 - `HotMercury`, `jason50123` 兩位作者從無到有開發了一個檔案系統 - SimpleFS > 很喜歡他們報告的副標題 - "simple but not easy to me" ### 效能實驗 * rw: write * runtime: 60 seconds * direct: 1 (direct I/O,繞過 page cache) * bs: 4KB (單次 I/O 操作的 Block Size) * size: 10MB (測試總大小) 使用以上參數,測試連續的寫檔表現,得到以下結論: * IOPS 意外的比現存的成熟 file system ext4, btrfs 等等還高 * 因為是 Blocking 操作,CPU 占用率非常高 * 直接操作讀寫 disk,所以 issued rwts 相當高 ### 分區結構 > 講者未深入介紹,依據現行檔案系統實作慣例補充 * super block: 儲存整個檔案系統的基本資訊 * inode store: 儲存所有 inode 的地方,inode 將用來作為檔案或目錄 data blocks 的索引 * inode bitmap: 標記 inode 的使用狀況,每個 bit 代表一個 inode 例如:`1 1 0 0 1 0 1 0` (inode `0`, `1`, `4`, `6` 被使用了) * block bitmap: 標記 data blocks 的使用狀況 * data blocks: 存放實際檔案數據的區塊,如文字或圖片等等 ### 刪除資料操作 原先刪除資料的操作,會需要讓後方剩餘的檔案去「填補」刪除後空缺出來的空間。 :::info 例: **1 -> 2 -> 3 -> 4 -> x** (原先的檔案結構) 若要刪除 `1`,則需要將 `2, 3, 4` 向前移動。 變成: **2 -> 3 -> 4 -> x -> x** 但這樣是相當耗費效能的操作;SimpleFS 的實作方法,是直接將刪除的 node 打上刪除標記,這樣就不必每次將後方的檔案 align 到新位置。 變成:**~~1~~ -> 2 -> 3 -> 4 -> x** ::: ### Journal 加入日誌系統,可以紀錄每次的操作,也能幫助 file system 從無預警的 crash 中復原。使用 jbd2 API 協作,分成三步驟: 1. 寫入檔案到 disk 的新區塊 2. 更新 journal 的 metadata(紀錄操作) 3. 更新 disk 的 metadata (inode 指向剛剛寫入的新區塊) ![image](https://hackmd.io/_uploads/HJ54_z5oke.png) 這樣可以保障做到一半 crash 時,有完善的恢復機制。 1~2 的過程 crash 時,因 journal 和 disk metadata 都還指向舊區塊,所以資料還是完好的、尚未寫入前的狀態。 2~3 的過程 crash,則可以透過 replay journal 來復原檔案。 > 關於講者提到的兩個 symlink bug,好像都是在 `rm` 之後發生,是否可能跟 rm 沒有正確更新 metadata 有關? ## 運用並行處理來強化網站資料存取的效率 - `yeh-sudo` ### Redis * in-memory database,檢索資料更快 * 使用 key-value 儲存資料 * single threaded 資料庫,在並行處理情況下有效能瓶頸 ### RCU (Read-Copy-Update) * linux kernel 裡的一種同步機制 * 允許同時多 reader,單一 writer 更新資料 * 在 linux 中,現存的 spinlock 和 semaphore 皆存在效能瓶頸 * User Space RCU,簡稱 URCU,不須強制運行在 kernel space 中 * URCU 共有五種 flavors,原作者使用 Single-based RCU * Single-based RCU:當 writer 寫入時,對所有的 reader 發送更新信號 ### 執行緒數量影響效能 發現執行緒增加時,效能不一定會隨之增加。透過實驗可以發現,可能是受到 cache miss 和 context switch 的影響。 解法:加入 CPU affinity 的設定,發現可以提升吞吐量。 (圖:加入 CPU affinity 設定前後的吞吐量比較) ![image](https://hackmd.io/_uploads/B1SnK2Kjyl.png) ### 我的反思 講者在後續講解 CPU affinity 與並行 Redis 效能的關係時,並沒有提到他用來測試的 task 為何。以 RCU 的機制來說,不同 reader/writer 比例狀況下,吞吐量、延遲應該都會有所不同。尤其在若有大量 writer 的狀況下,RCU 所提供的效能提升應該相當有限。這部分應該在講解的時候定義清楚比較好。 ## 高性能網頁伺服器 - `fatcatorange` ### CMWQ * 欲改善的問題:現有系統透過 kthread 來服務用戶,當請求發生時,每次都會需要創建新的 thread;這樣會造成很大的效能瓶頸 * 引進 CMWQ 之後,會預先創建 thread pool 並等待 task 分發 ### Directory Listing * 原先在做目錄存取的時候,每次點擊不同的目錄,都需要發送請求給 server * 使用 content cache,第一次存取目錄的時候就先全部放到快取中;後續直接從快取讀取即可,免去了每次都發送請求給 server 的時間成本 ### 引入 Timer 釋放逾時連結及快取 * 使用 priority queue 來維護已建立的節點資訊,包含過期的時間等等 * 隨時間經過,同時檢查 priority queue 中節點是否過期;若過期則執行 callback function、進行刪除 ### 我的反思 * CMWQ 部分,若是採用預先創建好的 thread pool,可能會造成 thread 數量和請求數量對不上的情況,若有大量請求時,latency 可能會很高;而沒什麼請求時背景也會有 thread 持續占用著系統資源。可以考慮加上「隨著流量動態調整 worker 數量」的機制去改善這點。 * 在測試 directory listing 的 cache 改善效果時只固定存取了同一路徑,應該要加入不同的路徑、隨機產生順序並進行多次測試比較準確。 (固定存取同一路徑,cache hit rate 一直保持在 100%,但現實情況不可能這麼理想) * Timer 部分,持續去檢查 priority 中的節點是否過期,是否會占用系統資源?在什麼樣的 time interval 下持續檢查,會達到最好的效能、同時確保過期的 node 不佔用記憶體資源太久? ## 實作多核 RISC-V 處理器模擬環境並運作 Linux 核心 - `ranvd` ### 目標 基於 semu(由老師開發的 RISC-V 系統模擬器),去實作 hart state management,以達成**多核** RISC-V 系統模擬;最終使用 Linux 來驗證正確性。 ### 專有名詞 * hart: 指的是硬體執行緒 (hardware thread),即 CPU core * SBI (Supervisor Binary Interface): 作業系統和 SEE 之間的 API 介面,確保無論 SEE 的實作如何抽換,SBI 提供的函式都能達到一樣的功能 * SEE (Supervisor Execution Environment): 在 Machine mode 下運行的軟體環境,負責實作 SBI 提供的函式功能 * semu: 輕量化的 RISC-V 系統模擬器。不同於其他模擬器,不需要執行韌體,可以直接在模擬器中提供 OS SBI 的函式使用 ### 開機方式 #### RISCV_BOOT_SPINWAIT Firmware 會將全部的 harts 給 kernel,由 kernel 決定其中一個 hart 來進行開機動作。 #### Ordered booting Firmware 只將其中一個 hart 給 kernel 執行開機動作,再由 linux kernel 透過 HSM 將其他 kernel 喚醒。 ### SBI HSM Extension hart 可以分為以下三個狀態: * stopped: hart 不在 supervisor mode 或更低優先權的模式運行;也可以是沒有在執行指令 or 斷電 * started: hart 正在運行 * suspended: 低功耗模式 or 沒有在執行指令 有以下四種方法可以控制 hart: `sbi_hart_start`, `sbi_hart_stop`, `sbi_hart_suspend`, `sbi_hart_get_status` ### CLINT/ACLINT 兩者皆為 RISC-V 平台上常見用來送 hardware & software interrupt 的硬體。 * ACLINT 可以產生 supervisor-level 和 software-level 的 interrupt * CLINT 只能產生 machine-level interrupt * ACLINT 更為模組化,不同功能硬體的 memory map 分離 ### PLIC * interrupt controller,用來處理外部裝置的 interrupt * 可以控制要將 interrupt 送到哪個 hart * 作者將程式改版,原實作方式僅支援單核處理 ### Atomic instructions #### Atomic Memory Operations 確保單一操作不會被其他指令打擾、中斷。 > 因為只有一條指令,所以不會失敗,但操作受限 #### LR/SC * LR (Load-Reserved): 讀取記憶體中的值後,設置一個 Reservation 保護標記 * SC (Store Conditional): 如果原地址的值未被修改 (即 Reservation 存在),則可以成功寫入;否則寫入失敗 > 用兩條指令完成,引入了錯誤重試的機制,提供更多操作靈活性 ## 打造具備網路連線的精簡虛擬機器 - `jimmylu890303` ### 目標 在運行虛擬機時,為 Guest OS 和 Host OS 之間創立網路連線的橋梁。 ### 虛擬機器的分類 * System VM: 需要提供整個作業系統所需要的功能,涵蓋硬體 (CPU, Memory, peripheral device) 虛擬化技術 * Process VM: 僅需要提供虛擬環境給 Process,例如 JVM ### Hypervisor 的分類 #### Type-1 (裸機式) * 直接運行在硬體上 * 效率較高 #### Type-2 (宿主式) * 運行在作業系統上 * 存取硬體資源須透過作業系統,有延遲問題 > 老師提及,影響效能的是實作手段,並非 Type-2 的效能一定較低 ### 名詞解釋 * KVM: 可將 kernel 轉換為 Hypervisor 的工具 * VirtIO: 虛擬化的 I/O device interface * VirtIO-net: VirtIO 提供的虛擬網路裝置 * TAP (TunAble Packet Interface),是 Linux 提供的虛擬網路介面, 有如 Guest OS 和 Host OS 之間的網路橋樑 ### Virtqueue 的三大區域 Guest OS 會透過 Virtqueue 向 Host OS 提出 I/O 請求,以下三個區塊可以協助兩個 OS 知道 I/O task 處理的狀態。 #### Descriptor Area 存放 I/O 資料的描述資訊,用來指向 Guest OS 記憶體中的 I/O buffer。 當 Guest OS 有新的 I/O 請求時,會在這裡建立新的資料。 ```c struct virtq_desc { le64 addr; // 記憶體位址(指向 I/O Buffer) le32 len; // Buffer len le16 flags; le16 next; // next descriptor }; ``` #### Available Area 會用來記錄尚未被處理的 I/O 請求,將可用的 Descriptor 的 idx 紀錄在此。 ```c struct virtq_avail { le16 flags; le16 idx; le16 ring[]; le16 used_event; }; ``` #### Used Area 用來記錄 Hypervisor 已經完成的請求的區域,讓 Guest OS 知道已處理完畢。 ```c struct virtq_used_elem { le32 id; le32 len; // 已處理的數據 len }; struct virtq_used { le16 flags; le16 idx; // descriptor idx struct virtq_used_elem ring[]; //queue le16 avail_event; }; ``` >疑問:`virtq_avail` 為何需要一個 `used_event` 欄位,以及 `virtq_used` 為何也需要 `avail_event` 欄位?兩個欄位看起來都和該 struct 的用途無關,一時也看不出用途,是否可以拿掉? ### Guest OS 的資料收發處理 使用兩個 thread 分別管理資料收發,兩者分別的任務如下: * 接收資料 (RX Thread):檢查 TAP 裝置是否有寫入事件;若有,則寫入到 virtqueue 中 * 傳送資料 (TX Thread):檢查 virtqueue 是否有資料要進行傳輸及 TAP 是否可寫;若兩者條件皆符合,則寫入到 TAP 裝置中 ### Virtio-net 工作流程 1. User appilication 欲傳送資料,先傳送給 driver 2. driver 將資料寫入 available buffer 3. virtio-net device 從 available buffer 讀取欲傳送的資料 4. 傳送資料到 Host OS Kernel space 中的 TAP 裝置 5. Network Stack 將資料傳送出去 ![image](https://hackmd.io/_uploads/HJh11-qi1l.png)