linux2023
contributed by < xueyang0312 >
linux-headers
套件已正確安裝於開發環境fibdrv.c
存在著 DEFINE_MUTEX
, mutex_trylock
, mutex_init
, mutex_unlock
, mutex_destroy
等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 POSIX Thread 的程式碼來確認。 搭配閱讀〈並行和多執行緒程式設計〉定義結構體 str_t, 以一個大小為 128 的字串來存計算結果。
add_str
是將 x
+ y
的值加起來,並將結果存到 result
先都以反轉的字串來處理,最後在將結果反轉回來
例如 0, 1, 1, 2, 3, 5, 8, 31, 12…
計算到 F500 (The first 500 Fibonacci numbers ) 也是正確的,結果如下:
應用 Fast Doubling 手法,實做遞迴版本:
搭配 bitwise 運算開發遞迴版的程式碼:
利用硬體指令 __builtin_clzll
計算出 k 開頭共有幾個 0,在以 63 - __builtin_clzll
為迴圈初始化。
可以看到 StringAdd
的方法,n
越大所耗費時間越多,因為需要透過 __copy_to_user
來將大數回傳到 user buffer。經過硬體加速指令 __builtin_clzll
所實做的 Fast_doubling_iterative_with_clz
版本,在四種實做方法所耗費時間是最少的。
但由於 long long 只有 64 bit 的關係, 我們分析的數據只能從 n = 0 ~ 92, 因此需要整合 bigNum 來增加數據的範圍。
令一個 unsigned int *number
的數字陣列來儲存,unsigned int
最大值為 4294967295
當超過這個最大值該如何表示? 例如:4294967316
所以,利用 bn
資料結構來計算大數運算,並且可以表示超過 unsigned long long int
的數字
由於大數沒辦法直接以數值的形式列出,這裡改用字串來呈現,轉換的部分利用 ASCII 的特性來組合出 10 進位字串
根據 Fast Doubling 最後結論為:
Given and , we can calculate these
所以,必須實做以 bn
為資料結構的大數運算:加法、減法、乘法、左移
一開始會先比較 a
和 b
的 sign
若爲一樣,則直接透過 bn_do_add
static function 將 a
、b
相加;若不一樣,則先比較兩者大小,並且確保 a
b
,再透過 bn_do_sub
static function 相減。
減法會將 b
直接變號,再透過 bn_add
function 來處理相減結果。
比較出 a
和 b
size 的最大值,並將結果 c
透過 bn_resize
function 來重新分配大小,由於 number
為 pointer to unsigned int
,所以會直接 truncate carry
~ ,再將 carry
又移 32 bits。
bn_do_add 實作
如果 carry
,則將 carry
+ 來確保 ,並將 carry
設為 1,表示借位。
bn_do_sub 實作
bn_mul_add 實作
修改 fib_read
以及 fib_write
,在 fib_read
會呼叫 fib_time_proxy
,並且根據 mode 來計算指定方法的時間,並且在 fib_write
回傳測量時間。
下圖為遞迴版本的 bn_fib_fast_doubling_iterative_clz 運算:
taskset
加上 -p
參數再加上該行程的 PID
-c
參數,讓 taskset
直接輸出 CPU 的核心列表/etc/default/grub
內的GRUB_CMDLINE_LINUX_DEFAULT
尾端,加入 isolcpus=15
來指定編號 15 的核心不被排程器賦予任務,修改完成後需 sudo update-grub
來更新設定,重開機後即生效PID 1
的 affinity list 不包含該編號 7 的 CPU/sys/devices/system/cpu/isolated
是否為指定編號 7insmod
將模組載入核心透過指定處理器親和性 (process affinity),可以將程式固定在特定的 CPU 中執行
performance.sh
:之後再用 $ sudo sh performance.sh
執行
針對 Intel 處理器,關閉 turbo mode
Pytohn script實做以及結果
4ce43c4
擷取自 ChatGPT 解釋
In perf stat
, the -r
option is used to specify the number of times to repeat the command or application and collect performance statistics.
And the -e
option is used to specify the hardware performance events to monitor during the execution of a command or application. These events can include CPU cycles, cache misses, branch instructions, page faults, and more.
To use -e
, you must specify the name of the hardware event you want to monitor using its event code. You can obtain a list of available events and their corresponding event codes by running the perf list
command.
輸入 perf list
來查看有哪些 event
可以觀察
bn_fib_fast_doubling_iterative_clz
先利用 perf stat
紀錄尚未優化的效能
branch-misses
並沒有被計算到,根據 ChatGPT 解釋,根據提示先 disableing NMI watchdog。
The NMI(Non-Maskable Interrupt) watchdog is a mechanism in the Linux kernel that monitors the system for hangs or freezes. When it detects such a condition, it triggers an NMI interrupt, which cannot be masked or ignored by the system. This interrupt takes priority over other interrupts and can cause perf to miss some events.
尚未優化的 bn_fib_fast_doubling_iterative_clz
目前效能如下:
mutex
對 linux kernel module 影響fibdrv.c
存在著 DEFINE_MUTEX
, mutex_trylock
, mutex_init
, mutex_unlock
, mutex_destroy
等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 POSIX Thread 的程式碼來確認。 搭配閱讀〈並行和多執行緒程式設計〉在 fibdrv.c 中,分別在 fib_open
以及 fib_release
使用 mutex_trylock
以及 mutex_unlock
。
mutex_trylock
是 linux kernel 提供的 mutex API, 並不像 mutex_lock
一樣會 block 住,而是會立即 return 成功或者失敗
當有 2 個 thread or process 以上嘗試同時 open fib character device 的話,只有一個能成功 open,只有當成功 open 的 thread or process,關閉 file descriptor,其他 thread or process 才能再次開啟。
以下程式來驗證:
可以觀察到第二個 thread 無法成功 open,因為第一個 thread 已經取得 mutex 了
查看 dmesg
若將 file descriptor 讓 thread 共享,所以 struct file
當中的 f_pos
會成為共用資源。
thread 2 以 lseek
設定好目標 offset 後會休眠 2 秒,但在休眠的期間 thread 1 又更改了 offset,導致 thread 進行 read 時使用的 offset 不是當初設定的數值,產生 race condition。
在 __init
加入印出MAJOR
, MINOR
兩個設備號碼:
dmesg
查看成功註冊 fibonacci
character device 訊息
Major number
為 509,這個數值可以對應 $ cat /proc/devices
結果。
在 <linux/kdev_t.h> 定義 MAJOR
, MINOR
macro:
kernel 必須避免兩個設備驅動使用到同一個主設備號的情況,linux kernel 提供兩種 interface function 完成設備號的申請:
register_chrdev_region() 需要指定主設備號,在使用這 function 前,Programmer 要保證分配的主設備號沒有被人使用。
所以 linux 提供了另一個 function:
alloc_chrdev_region() 會自動分配一個主設備號,可以避免和系統佔用的主設備號重複。
在卸載 module 時,需要把主設備號釋放給系統:
首先,先解釋 struct cdev
資料結構
cdev_alloc()
: 分配一個記憶體空間,並做初始化。
cdev_alloc
也可以替換成: kmalloc
配置記憶體給 fib_cdev ,在呼叫 cdev_init(struct cdev *cdev, const struct file_operations *fops)
,但是 cdev_init
要傳入 file_operations。
cdev_add()
:將 char device 加入至系統。
到目前為止,尚未在 /dev/
目錄下建立 device,有兩種方法可以建立
fibonacci
的 device driverclass_create
、device_create
來在 /dev/
目錄下建立 fibonacci char device:device_create
時,要傳入 struct class
當作 argument,所以必須先 create class, 而這個 class 會放置在 sysfs
下面(/sys/class/fibonacci),就不需要透過 mknod
來手動建立 device driver, 在呼叫 device_create
成功後,就會在 /dev/
目錄下建立 char device,也就是分配 inode
節點。
總結剛剛所述,在 fibdrv.c 中定義了一系列對於此 char device 的操作:
定義完操作後變將這些操作關聯到相對應的 char device 上,並註冊 char device 到系統當中,
最後就是建立 device node,讓 user 可以透過對這個 device node 操作,來和 device 互動
VFS 定義了在實體檔案系統上更高一層的介面,讓應用程式得以透過 VFS 定義好的介面存取底層資料,不用考慮底層是如何實作。有了 VFS,增加擴充新的檔案系統非常容易,只要實作出 VFS 規範的介面。