contributed by < Ahsen-lab
>
fibdrv.c
存在著 DEFINE_MUTEX
, mutex_trylock
, mutex_init
, mutex_unlock
, mutex_destroy
等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題。嘗試撰寫使用 POSIX Thread 的程式碼來確認。 搭配閱讀〈並行和多執行緒程式設計〉若在 Linux module 中使用 variable-length array (VLA) 的機制,編譯過程中會產生警告。因此,為了解決此問題,改用固定大小的陣列來實作求解 Fibonacci 數列的迭代演算法。經修改後,程式碼可以正常編譯和執行。
在新的實作中,迭代演算法從第二項開始進行計算,而不再是從第一項開始。
引入大數運算的實作,用來計算 以後的數值。
bn
結構體number
- 指向儲存的數值,之後會以 array 的形式來取用size
- 配置的記憶體大小,單位為 sizeof(unsigned int)
sign
- 0 為正數、1 為負數bn_to_string
bn_fib_fdoubling
使用 Fast Doubling 的技巧,在大數運算中計算 Fibonacci 數。
dest
- 用來儲存 n
- 找出第 個 Fibonacci 數首先確保 dest
的 size 為 1,若 n < 2
則 。
初始化 f1
和 f2
為 0
和 1
,分別對應 和 ,再給定兩個變數 k1
和 k2
分別用來儲存 和 。
根據上述公式並配合 Bottom-up Fast Doubling 的做法,假設要找第 個 Fibonacci 數,只需要從左到右依序檢查每個位元,步驟如下:
k1 = fib(2k)
、 k2 = fib(2k+1)
i
,若不存在的話跳到第 3 步,否則(假設目前為 ):
i = 0
:
f1 = fib(2k) = k1
f2 = fib(2k+1) = k2
i = 1
:
f1 = fib(2k+1) = k2
f2 = fib(2k+2) = k1 + k2
f1
也就是 對應的程式碼:
為了分析並改善 Fibonacci 數的計算效率,需要測量並量化執行時間,我們會使用 fib_read()
來計算 Fibonacci 數列,使用者可在 user space 透過指定不同的 offest 作為 Fibonacci 數列的 然後透過 read
來輸出 。
在計算的過程中,傳入 fib_read()
的 size
參數並沒有被使用到,因此我將 size
當作選擇演算法的參數:
bn_fib_fdoubling()
在 case 1
中,首先用 bn_alloc
創建了一個新的大數 fib
(用於計算 Fibonacci 數列),然後調用 bn_fib_fdoubling
函式來計算第 *offset
項的 Fibonacci 數。計算完成後,使用 bn_to_string
將結果轉換為字串,並用 copy_to_user
將其複製到 user space 的 buf
中,最後回傳剩餘的未複製成功的字節數。如果一切正常,該函式回傳 0。
在 Linux 核心模組中,使用 ktime 系列的 API 來測量 Fibonacci 數列計算的時間,參考 核心模式的時間測量 的做法
kt
為在核心模式下執行 bn_fib_fdoubling(fib, *offset)
的時間,首先,在計算之前先用 ktime_get()
取得當前時間,在計算完成後,再次呼叫 ktime_get()
,用計算後的時間減去計算前的時間,即可得到在核心模式中 Fibonacci 數列計算的時間。
Fibonacci 數列計算的結果會儲存在 buf
裡面,需要透過 copy_to_user
函式來傳回 user space , k2u
會儲存資料從 kernel 傳遞到 user space 的時間。
這段程式碼的目的是將 Fibonacci 數列計算的結果傳回給 user space,並測量從 kernel 傳遞到 user space 所需的時間。
copy_to_user
函式用於將計算結果從 kernel 的記憶體區域 p
複製到 user space 的緩衝區 buf
中。在這之前,使用 ktime_get()
函式獲取當前時間,在傳遞結束之後,再次呼叫 ktime_get()
函式,獲取傳遞後時間點,並使用 ktime_sub()
函式計算兩者之差,即為從 kernel 傳遞到 user space 所需的時間,儲存在 k2u
變數中。
read()
在 user space 的執行時間fibdrv
是一個 character device ,使用者可透過定義相關的函式並使用存取檔案的系統呼叫 (如 read()
) 來與裝置互動,也就是說使用者空間 ( userspace ) 的程式可透過 read()
系統呼叫來得到 fibdrv
裝置的輸出。
在 user space 中,我使用 clock_gettime(3)
函式來計算 read()
系統呼叫的執行時間。
clk_id
參數可用來指定不同的計時器
CLOCK_PROCESS_CPUTIME_ID
: 當前進程在 CPU 上執行的時間。這個計時器不會包括行程被阻塞的時間,因此可以更好地測量行程的實際執行時間。
具體而言,首先獲取當前時間點,稱為 start
,接著執行 read()
系統呼叫,獲取讀取的字元數 sz,最後再次使用 clock_gettime()
函式獲取當前時間點,稱為 end 時間點。使用 end 減去 start 可得到 read() 系統呼叫在使用者空間中的執行時間。