contributed by < howardjchen >
lsmod
的輸出結果有一欄名為 Used by
,這是 "each module's use count and a list of referring modules",但如何實作出來呢?模組間的相依性和實際使用次數 (reference counting) 在 Linux 核心如何追蹤呢?
搭配閱讀 The Linux driver implementer’s API guide » Driver Basics
fibdrv.c
存在著 DEFINE_MUTEX
, mutex_trylock
, mutex_init
, mutex_unlock
, mutex_destroy
等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 POSIX Thread 的程式碼來確認。由於原本的 fibdrv 只能跑到 offset=92 就會 overflow ,這邊先著手進行修改
的計算結果用 16 進位表示是 0x1 11F3 8AD0 840B F6BF
,超過 64 位元整數所夠表示的最大數值,因此考量到要正確輸出 或數列更後面的數值,就必須使用到特製的結構來處理回傳值。我們利用作業裡的例子進行實作
使用 struct BigN 來將一個數字分成兩部份:
進行大數加法時,則需要注意 lower 是否需要進位到 upper:
利用 128 bit 可以表示到 offset=186
我們先針對 fib_sequence
來進行時間量測
我們利用 write 來回傳計算的時間
於是就有量測時間的結果,但量測數值有點浮動
參考 KYG-yaya573142 的報告 修改 /etc/default/grub
內GRUB_CMDLINE_LINUX_DEFAULT
,加入 isolcpus=5
來指定編號 5 的核心不被排程器賦予任務,修改完成後需 sudo update-grub
來更新設定,重開機後即生效 (可從 /sys/devices/system/cpu/isolated
確認是否生效)
修改後可觀察到 PID 1 - init 的 affinity list 不包含該編號的 CPU
透過指定處理器親和性 (processor affinity,亦稱 CPU pinning),可以將程式固定在特定的 CPU 中執行
準備以下 shell script 來設定 CPU 以最高頻率執行所有 process
關閉 Intel 處理器的 turbo mode
執行上述步驟後進行量測,發現結果仍有飄動的情況
針對 SMP IRQ affinity 進行設置,盡量避免 CPU 5 去處理 IRQ。使用以下 script 進行設置,僅將 CPU 5 從可用清單去除,但不大幅度改變本來系統的設置 (例如 smp_affinity 原本是 0~15,只會被更改為 0~4, 6-15)
註: smp_affinity 和 smp_affinity_list 擇一設定即可
設置完畢後可以透過 cat /proc/interrupts
觀察 CPU 5 的 IRQ 數量,同時也可以量測到更穩定的實驗結果
使用 clock_gettime
來取得時間
使用 ktime 來量測執行時間,這裡參照作業說明的手法,挪用 fib_write
來回傳 kernel space 的執行時間
使用上述量測時間的方式中提到的方式分別量測 user space 及 kernel space 花費的時間,再將兩者相減即可獲得 user space 與 kernel space 的傳遞時間
假設數據分佈接近常態分佈的情況下,一個標準差到三個標準差之內的資料量約佔 68%, 95%, 99.7%
圖片來源: wikipedia
我們採用每次取100個樣本並留下 95% 的 data 取平均,可以發現畫出來的圖形平滑許多,不會再有漂移的情況
研讀上述費氏數列相關材料 (包含論文),摘錄關鍵手法,並思考 clz / ctz 一類的指令對 Fibonacci 數運算的幫助。請列出關鍵程式碼並解說
指出針對大數運算,有哪些加速運算和縮減記憶體操作成本的舉措?
lsmod 的輸出結果有一欄名為 Used by,這是 “each module’s use count and a list of referring modules”,但如何實作出來呢?模組間的相依性和實際使用次數 (reference counting) 在 Linux 核心如何追蹤呢?
搭配閱讀 The Linux driver implementer’s API guide » Driver Basics
注意到 fibdrv.c 存在著 DEFINE_MUTEX, mutex_trylock, mutex_init, mutex_unlock, mutex_destroy 等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 POSIX Thread 的程式碼來確認。
複習 C 語言 數值系統 和 bitwise operation,思考 Fibonacci 數快速計算演算法的實作中如何減少乘法運算的成本;
在 GitHub 上 fork fibdrv,目標是修正 Fibonacci 數計算的正確性 (現有實作存在缺陷,請指出),隨後改善 fibdrv 計算 Fibinacci 數列的執行效率,過程中需要量化執行時間,可在 Linux 核心模組和使用層。
- 原本的程式碼只能列出到 而且部分輸出是錯誤的數值,請修改程式碼,列出後方更多數值 (注意: 檢查正確性和數值系統的使用)
由於原本的 fibdrv 只能跑到 offset=92 就會 overflow ,這邊先著手進行修改 的計算結果用 16 進位表示是 0x1 11F3 8AD0 840B F6BF
,超過 64 位元整數所夠表示的最大數值,因此考量到要正確輸出 或數列更後面的數值,就必須使用到特製的結構來處理回傳值。我們利用作業裡的例子進行實作
為了要增加運算的位數值,這邊實作個字串相加的 function
然後我在們 fib_read
的時候利用copy_to_user
將運算的結果傳到 user space buffer
client.c
目前可以成功計算到 offset=500
TODO: 發現在 kfree 我們 kmalloc 出來的 ptr的時候會有 general protection fault 的 error
用 printk 固然方便,但其執行時間易受各種因素而不穩定,除了透過統計來觀察,也可改用核心的 sysfs 介面,後者可讓我們自行宣告 show 和 store 介面來提供讀寫操作,可參見 Sample kobject implementation (注意: 切換到對應的 Linux 核心版本)。
逐步最佳化 Fibonacci 的執行時間,引入費氏數列分析 提到的策略,並善用 clz / ffs 一類的指令 (Linux 核心有對應的包裝函式),過程中都要充分量化
嘗試研讀 bignum (
fibdrv
分支) 的實作,理解其中的技巧並與你的實作進行效能比較,探討個中值得取鏡之處。
- 原理和分析可見 KYG-yaya573142 的報告
當 隨著 越來越大時,由 Linux 核心傳遞字串形式的十進位就成為效能瓶頸,研讀〈Integer Encoding Algorithm 筆記〉所提及的策略,在 Linux 核心模組先對數值進行編碼和壓縮,以二進位方式傳遞到使用者層級後,再由應用程式予以顯示 數值
- 儘量降低由核心傳遞到應用程式的資料量
- 確保應用程式不需要過多的計算量才可重現 Fibonacci 數列