contributed by < ollieni >
在本機和虛擬機器進行實驗的差別為以下兩點:
根據老師提供的文件,我們可以得知我們要看的程式在 kernel/module/main.c,在其中可以看到一個 function find_symbol
,裡面有使用 List API list_for_each_entry_rcu
去找到 symbol。
在 resolve_symbol
這個 function 裡面,會由以下程式去檢查是否 GPL_only
在 inherit_taint
function 裡有做確認是不是 GPL only,如果不是,會輸出錯誤訊息。
用 strace
追蹤 Linux 核心的掛載:
用 execve
啟動一個新的進程來執行指定的程序,執行 /sbin/insmod
,以 ["insmod", "fibdrv.ko"] 作為參數傳入。
用 getcwd
獲取當前工作目錄,然後尋找要加載的模組文件 /tmp/fibdrv/fibdrv.ko
。
用 openat
打開了模組文件,並進行文件相關的操作(fstat
和 mmap
)。
最後,使用 finit_module
將模組加載到核中。
simrupt 裡使用的 mutex lock 有三個,read_lock
、producer_lock
、consumer_lock
。
以下是三個 mutex lock 個別的作用 :
read_lock:
作用:保護共享的 kfifo 緩衝區 (rx_fifo),防止多個使用者空間的讀取操作同時進行。
在 simrupt_read() 函式中使用,透過 mutex_lock_interruptible(&read_lock); 取得此鎖,確保只有一個進程或線程能夠執行讀取操作。
producer_lock:
作用:用於序列化對 kfifo 緩衝區的寫入操作,確保只有一個生產者(在工作序列處理函式 simrupt_work_func 中)能夠訪問並向緩衝區添加新數據。
在 simrupt_work_func() 函式中,透過 mutex_lock(&producer_lock); 取得此鎖,以確保在工作隊列中只有一個生產者能夠向 kfifo 緩衝區寫入數據。
consumer_lock:
作用:保護 fast_buf 緩衝區,在工作序列處理函式 (simrupt_work_func) 中確保只有一個消費者能夠從 fast_buf 中提取數據。
在 simrupt_work_func() 函式中,透過 mutex_lock(&consumer_lock); 取得此鎖,以確保在工作隊列中只有一個消費者能夠從 fast_buf 中取出數據。
閱讀老師給的文件 並行程式設計: Lock-Free Programming 後,得知了有 lock-free 和 lock-less 的區別,以下是我的理解。
相同 : lock-free 和 lock-less 都避免使用 lock 。
相異 : lock-less 注重資料共用可以正確和安全,lock-free 則更重視執行序要有進展。
根據 CMWQ 文件中所說,可以使用 alloc_workqueue()
對 work queue 設置一些 flag,包括 CPU 位置、併發限制、優先順序,另外, 可以用 apply_workqueue_attrs()
,這個 API 對 unbound work queue 設置屬性。
Linux 提供了 CPU Affinity 的概念,允許用戶將特定的任務或線程綁定到特定的 CPU 核心上運行。可以通過 apply_workqueue_attrs()
,對特定的 work queue 修改 affinity_scope 或 affinity_strict 來實現。
在文件中,以 affinity_scope 的預設值 cache 的情況舉例,在這個情況下,會根據上一級快取的界限將 CPU 分組。 work queue 上排隊的 work item 將會被分配給一個與發出 CPU 共享上一級快取的 CPU 上的 thread。
An unbound workqueue groups CPUs according to its affinity scope to improve cache locality. For example, if a workqueue is using the default affinity scope of “cache”, it will group CPUs according to last level cache boundaries. A work item queued on the workqueue will be assigned to a worker on one of the CPUs which share the last level cache with the issuing CPU. Once started, the worker may or may not be allowed to move outside the scope depending on the affinity_strict setting of the scope.
和 CPU scheduler 的互動方式是將 CPU 的 worker-pool 接入 scheduler 來管理,確保並行性。每當 worker 被喚醒或進入睡眠狀態時,都會通知 worker-pool,以便追蹤當前可用的 worker 數量。此外,工作項目不應該佔用 CPU 太久。只要 CPU 上有多個 worker 正在執行,它就不會開始執行新的工作。但是當某個正在運行的 worker 進入睡眠狀態時,worker-pool 會立即分配一個新的 worker,這樣 CPU 就不會在還有待處理的工作項目時進入空閒狀態。
Each worker-pool bound to an actual CPU implements concurrency management by hooking into the scheduler. The worker-pool is notified whenever an active worker wakes up or sleeps and keeps track of the number of the currently runnable workers. Generally, work items are not expected to hog a CPU and consume many cycles. That means maintaining just enough concurrency to prevent work processing from stalling should be optimal. As long as there are one or more runnable workers on the CPU, the worker-pool doesn’t start execution of a new work, but, when the last running worker goes to sleep, it immediately schedules a new worker so that the CPU doesn’t sit idle while there are pending work items. This allows using a minimal number of workers without losing execution bandwidth.
在論文中有看到 xoroshiro128+
的程式碼 :
可以看到用了三個 bit-wise 操作, xor 、 rot(circular shift) 、 shift。
理論概念主要是透過這些 bit-wise operation,使數值在高維中分散和混合。
在 sort_mod.c 裡面,會在 sort_init
函式裡面用 alloc_workqueue
創建並設定 work queue ,另外,會在 sort_exit
函式裡面使用 destroy_workqueue
去安全的刪除 work queue。
另外,會通過以下兩行程式,用 module_init
這個宏將 sort_init
、sort_exit
,設為模組初始化函式,將會被 kernel 自動調用,執行模組所需的初始化過程。
在 sort_impl.c 裡可以看到有一個 init_qsort
函式,使用了 INIT_WORK
這個宏。 INIT_WORK
會使用
module_init
設定的初始化函式,將 work 初始化,以便於將這個工作結構提交到 work queue 中等待執行,因此在程式中可以看到在init_qsort
函式後,都接著一個 queue_work
函式,將工作放進 work queue 裡面。