contributed by < Destiny0504
>
交換二個指標所指向的字元空間
將字串順序反轉
將兩個數字字串相加
利用 string_number_add
計算 fibonacci sequence 中的數。
copy_to_user
將我們算出來的結果從 kernel space 複製一份到 user space 。
fibdrv.c
要做為一個 kernel module ,所以必須去 linux document 找出有同樣功能的 function 來使用 ( e.g. memmove
、kmalloc
、krealloc
)。Fib(100000)
的數
krealloc
,會拉長執行的時間,所以在改進的部份有減少使用krealloc
,嘗試確認 krealloc
影響的多寡。Fib(0)
~ Fib(100)
每一個數字所需的時間
後續章節改進:krealloc
會拖累程式的執行速度,所以應該盡量減少使用
因為 fast doubling 的 Fibonacci sequence 公式有減法與乘法,所以我們需要實作大數乘法與大數減法。
將兩個數字字串相乘
將兩個數字字串相減
由 fast doubling 的公式可以得知在遇到第一個 1 之前,fib_0
與 fib_1
所紀錄的值並不會改變,所以使用 __builtin_clz
可以直接得出由右開始計算的第幾個 bit 為 1 ,能夠讓迴圈的執行次數變少,達成加速的效果。
Fib(0)
~ Fib(100)
每一個數字所需的時間
unsigned int pointer
來儲存大數 (fibdrv
的分支),減少了記憶體的使用量。
1 byte
來儲存一個數字,他的實作能夠用 4 bytes
來存放 realloc
的次數提醒:Karatsuba algorithm 一定要配合 的方式去計算,否則需要花費的乘法計算次數不會比較少,而且上面的分析沒有考慮到加法的成本,所以當數字很小,有可能沒有省到計算時間。
krealloc
的使用相較於原本的版本,新的版本減少了一次 memmove
一次 krealloc
,所以執行時間的可以看出不小的差距。而且也沒有出現執行時間浮動的問題。
/etc/default/grub
內的 GRUB_CMDLINE_LINUX_DEFAULT
,加入 isolcpus=15
來指定編號 15 的核心不被排程器賦予任務,修改完成後需 sudo update-grub
來更新設定,重開機後即生效,可以用 taskset -cp 1
命令得知特定任務的 CPU affinity。相比於去除干擾因素前,可以看出更明顯的線性關係
相比於去除干擾因素前並無太大的區別
access_ok
保護 kernel space
access_ok
的用意在於確認 buf 這個指標所指向的位置確實在 user space 以免出現系統漏洞,讓惡意使用者可以更改 kernel space 的內容綠色的線所代表的是 copy from user 所需要的時間
紫色的線所代表的是 copy to user 所需要的時間
access_ok
的執行時間比較,這就說明了記憶體區塊檢查所花的時間並不多Fib(0)
與 Fib(1)
永遠是相同數字,就可以確保結果的正確性。Fibonacci number
時的位數只會不斷的上升,所以我們可以嘗試使用位數估計的方式,去估計我們的結果的正確性。在 C 語言 int
只佔有 4 bytes 所以任何整數加上 0 與加上 會得出一樣的結果,這不符合前面提及的 well-defined ,但因為 python 具有大數處理的能力,不會有任何整數加上兩個不同整數卻可以得到相同的答案,所以我們可以先用 python 的程式碼計算出結果,再與我們用 C 語言實作的結果進行比對驗證我們計算的數值正確性。
驗證前 1000 個 Fibonacci 數的 python 程式碼
可以透過 Fibonacci sequence 的遞迴關係式得出 Fibonacci 數的一般式如下 :
我們要估計的是位數,所以將上式的 取 來得到估計位數值
fib_fast_doubling
和 fib_sequence
Robert Love 在 "Linux Kernel Development" 提到浮點數時是這樣說的
No (Easy) Use of Floating Point
When using floating-point instructions kernel normally catches a trap and then initiates the transition from integer to floating point mode. Unlike user-space, the kernel does not have the luxury of seamless support for floating point because it cannot easily trap itself. Using a floating point inside the kernel requires manually saving and restoring the floating point registers. Except in the rare cases, no floating-point operations are in the kernel.
目前觀察到的用處,但還需要進一步的檢驗
fib_open
中有 mutex_trylock(&fib_mutex)
檢查有沒有其他的 thread、process 打開這個 module,但其實並不需要這樣做,因為取得 Fib(offset)
的過程中,只有 offset
是共享的。fib_write
並沒有任何的功能,所以不需要任何的保護,但如果我們要測試 copy_from_user
的話就需要用mutex_trylock
保護共享的變數。mutex
保護的只有 fib_read
的 offset
fibdrv
,只要將要執行的 work 掛載於 workqueue 上,等待 work 被執行,進而不再限制打開 fibdrv 的 process 數量。在 fib_read
之中,有一個參數並沒有被用到,其實也可以用這個方式來決定要讀取的數值,並不需要利用 offset
fache
)甚至實作了自己的 thread pool 。cmwq
提供了 apply_workqueue_attrs()
來讓 user 自己進行調整。因為前面有設定 cpu affinity 的緣故,所以 /sys/devices/virtual/workqueue
的 mask
被改為 ffff7fff
=> 第 15 個 bit 被改為 0 了
struct work_struct *
作為傳入的變數,所以當我們需要傳入額外的變數 (e.g. offset) ,就可以用指向 kfib
的指標來取得 offset
的值。我們只需要利用 container_of
就可以在只有 struct work_struct *
得到指向包含這個 work_struct
的 kfib
的指標offset
是整個 file descriptor 的共用變數,所以當我們要同時讓兩個以上的 process 使用這個 character device 時,要讓這個變數不再被共用,而這裡的作法便是將 offset 與 pid 同時儲存起來,未來要執行放入的 work 的時候,只需要比對 pid 即可找出對應的 work 並執行
list
作用在於串接於整個 fibdrv
所維護的額外的 linked list, 利用 thread ID 不會重複的特性,當執行 fib_read 時,只要在 list 上找出 pid 與現在執行 fib_read 相同者,就可以知道當初放入 workqueue 時的 offset
是多少用這個 function 來呼叫已經實作好的 fib_fast_doubling
,並在執行完之後釋放 worker 所佔用的記憶體。
我們為了讓只能傳入work_struct
的 work_fn
可以得到別的參數所以需要用這個 function 初始化一個 kfib
並回傳其中的 fib_work
變數,來將其掛載於我們建立的 workqueue
spin_lock_irq()
。應該增加新的機制 (e.g. timer),避免 process 一直不執行 fib_read
所導致的串列過長問題
由於移動 offset 就代表未來 fib_read 會需要讀取相對應的 fiboncci number ,所以需要將 offset 、 pid 儲存起來並掛載於串列中。同時為了避免一個 process 或是 thread 更改 offset 多於一次,所以在這樣的情況發生時,將採取不將新的 offset 放入串列之中的作法。
需要採取更新 offset 或是無視此次移動 offset 兩種方案的哪一種仍有待商榷,兩者的差別在於想要以過去的想要讀取的值為主,還是以新的值為主。
除了建立自己的 workqueue 之外,也可以使用系統的 default workqueue ,default workqueue 是由系統維護,也是一個常用的 workqueue 使用方法,所以此專案中介紹。
不再由 mutex lock 避免 fibdrv
被同時開啟
利用以下程式碼可以檢驗 workqueue 的實作是否有效
原本的執行結果可以很明顯的看到,沒有 mutex 的保護會導致回傳值出錯
利用 workqueue 機制增加吞吐量的同時,確保正確性
若 userspace 欲同時存取 fibdrv 的執行緒數量大於 CMWQ 的 worker 數量,會發生什麼事?
jserv
如果想要存取 fibdrv 的 thread 數量,大於目前 CPU 所擁有的 worker 數量,CMWQ 會建立新的 worker 來執行需要執行的 work 來提升整體的產出量。根據官方文件寫說,worker 只是一個 kernel thread ,佔用的資源並不多,所以這樣的處理方式也是合理的。
假設 w0, w1, w2 全部被 queue 在 wq q0 上,max_active 被設定為 2
Linux 官方程式碼 :linux/workqueue.h
Linux 官方程式碼 :kernel/workqueue.c