contributed by < jeremy90307 >
花了些許時間閱讀,把相關問題及重點紀錄在這 ––> The Linux Kernel Module Programming Guide 筆記
參考 : 2021 年的筆記
解釋
kfifo 使用 in 和 out 兩個變量作為進入和離開的索引,如圖
+ n
- n
在 simrupt.c 的註釋中解釋為何無須再使用額外的鎖
使用到的 kfifo API
DECLARE_KFIFO_PTR
— macro to declare a fifo pointer object
kfifo_in
- put data into the fifo
kfifo_to_user
— copies data from the fifo into user space
kfifo_len
— returns the number of used elements in the fifo
kfifo_alloc
— dynamically allocates a new fifo buffer
kfifo_free
— frees the fifo
更多 FIFO 的 API 參考 -> kfifo
在 /include/linux/kfifo.h 可以看到 kfifo 的結構體
kfifo_in
kfifo_unused(fifo)
用作查詢剩餘大小,若進入的大小大於剩餘大小,則以剩餘的大小為主 len = l
,最後 kfifo_copy_in
複製資料至記憶體。
fifo->mask + 1
為整個佇列的大小減去已用大小 fifo->in - fifo->out
為何使用兩次 memcpy
?
kfifo_alloc
當中 roundup_pow_of_two
可以求出 size 向上最接近的 2 的冪次
程式碼取自 /include/linux/log2.h
當中 __builtin_constant_p(n)
函式中的 n 若為常數回傳 1 ,反之為 0 。
參考
-> 並行程式設計: Ring buffer
-> circular buffer
先看註釋描述,看似要做出一個更快速的 circular buffer ,並結合 kfifo 。
更快速?
查找 /include/linux/circ_buf.h 來了解 simrupt 中 circular buffer 使用到的 API
結構體
buf
: real pointer that contains the arrayhead
,tail
: head and tail indicesCIRC_CNT
回傳 buffer 被佔用的大小
緩衝區中總是有一個儲存單元保持未使用狀態。因此緩衝區最多存入
size - 1
。
CIRC_SPACE
回傳緩衝區剩餘大小
fast_buf_clear
head = tail = 0 表示均指向同個位置,因此 buffer 為空。fast_buf_get
fast_buf_put
中斷概念參考 Linux 核心設計: 中斷處理和現代架構考量
為防止 interrupt 產生打架的情況,分成三種類型:
在 Linux 作業系統中,使用了三種機制去實現 Bottom Half
softirqs 和 tasklet 執行於 interrupt context , workqueue 執行於 process context 。
相關問題:
Difference between interrupt context and process context?
在 simrupt 中
定義 tasklet
加入 tasklet
刪除 tasklet
在 simrupt 中建立 workqueue
當中 WQ_UNBOUND
表示不綁定任何 CPU
更多 Workqueue flags 參考 /include/linux/workqueue.h
執行指定的 workqueue 任務,當中 &work
指向具體任務的指針。
釋放 workqueue
當 producer 將數據儲存至 kfifo buffer 及 consumer 將數據從 circular buffer 取出時使用互斥鎖。
使用 timer_handler
函式來模擬中斷的發生,在函式中 local_irq_disable
用於停用中斷,來避免中斷時又遇到中斷的發生,接著將延遲中斷的部份使用 process_data
將字元放入 circular buffer ,並啟用 tasklet 來處理中斷,最後使用 local_irq_enable
函式再次啟用中斷。
在 simrupt 中定義的鎖
註釋指出,在特定條件下,即只有一個並行 reader 和一個並行 writer 時,對 kfifo 的使用是安全的,無需額外的鎖定機制。
考慮到並行,因寫入操作是由 interrupt context 進行(不可被搶佔、sleep),因此只有一個 writer 可以存取 kfifo,但讀取操作就必須使用 DEFINE_MUTEX(read_lock)
來限制只能有一位讀者存取 kfifo ,總結最後確保 kfifo 的並行是安全的,在同一時間只有一個 writer 及 reader 可以存取 kfifo 。
藉由 kernel thread 取得 circular buffer 的資料,使用 mutex 來避免執行緒之間在取得資料時發生問題
疑問
前面的註釋提到寫入 kfifo 的操作在 interrupt context 已確保同時只能有一個寫著存取,為何這裡還要在上鎖?
當 circular buffer 為空時 if (val < 0)
喚醒等待中的佇列
參考江冠霆同學的作業時發現他整理了一個流程圖,可以加速認識 simrupt 的運作原理,後續回頭複習時能更快了解。
用粗粒(coarse-grained)細粒(fine-grained)來舉 lock 的優劣
x++
,x--
-> 確保不會被 CPU 交錯執行if (x == y) x = z;
若傳入為符合預期的值進行 swap ,簡單的程式碼,重點在於 ATOMIC
並非無鎖的操作就是 lock-free
討論能否改成 lock-free
下方程式碼使用 mutex 確保從 circular buffer 取得的值是一致的
在 fast_buf_get
中或許可以使用 atomic 的 CAS 操作來改寫,但具體怎麼改寫還在思考。
因目前設備使用的 kernel 核心版本為 v6.5.0,為了後續實作查看 Linux kernel v6.5.0 的源程式碼
使用 strace 追蹤執行 insmod hello.ko 的過程有哪些系統呼叫被執行:
當中的 finit_module
在 kernel/module/main.c 中可以找到,接著呼叫 idempotent_init_module
-> init_module_from_file
-> load_module
,最後呼叫的 load_module
為 Linux 核心配置記憶體及載入模組。
接著查看到 load_module
的程式碼,-> simplify_symbols
-> resolve_symbol_wait
-> resolve_symbol
-> find_symbol
-> list_for_each_entry_rcu
list_for_each_entry_rcu
是一個 RCU 的 List API