contributed by < JeepWay
>
參考文章:(Linux kernel模組的開發) 編譯 Linux kernel module
參照指定的教材 (你也可因此做出貢獻),而非這類過時資訊。
Linux 核心模組運作原理
Makefile 裡面的兩個子目標
kmod
kxo.ko
。xo-user
kxo.ko
互動的 user space 程式。TARGET
:模組的名稱,這裡是 kxo
。kxo-objs
:列出了組成 kxo
模組的所有目標檔案(.o)。一定要取名為 kxo-objs
,不能是其他名子,例如 kxo1-objs
。否則會出現以下:
obj-m
:表示要編譯成核心模組的檔案名稱集合,這邊是 kxo.o
。$(KDIR)
:指定 Linux 核心的建構目錄,例如 /usr/src/linux-headers-6.8.0-56-generic
。-C $(KDIR)
:告訴 make 切換到核心的建構目錄。M=$(PWD)
:指定要編譯的模組原始碼的目錄 (即專案目錄)modules
:是核心編譯系統的目標,表示編譯模組,觸發模組編譯流程。[M]
:kbuild 系統中的標記,表示這些檔案是為核心模組編譯的,而不是核心本身的內建部分。這些 .o
檔案最終會成為一個獨立的 .ko
檔案,而不是直接連結到核心 image 中。[M]
,表示該檔案是核心內建部分的編譯(例如編譯 vmlinux
時)。CC [M]
編譯指定的 .c
原始碼檔案成 .o
目標檔案。LD [M]
將 main.o
、game.o
、xoroshiro.o
等目標檔案(由 kxo-objs
定義的檔案)連結成 kxo.o
,並最終生成 kxo.ko
,成為一個可以在 Linux 核心啟動後支援動態插入 sudo insmod kxo.ko
和卸載的核心模組。模組的設計允許機器在不重新編譯或重啟核心的情況下新增功能,這是 Linux 模組化架構的強大之處。MODPOST
處理模組的符號表,生成 Module.symvers
,記錄模組中導出的符號(例如函式),以便其他模組使用。kxo.mod.c
,包含模組資訊(如名稱、版本、參數等),生成 kxo.mod.o
。kxo.o
和 kxo.mod.o
連結成最終的核心模組檔案 kxo.ko
,即要載入核心的檔案。BTF
(BPF Type Format)是核心用於調試和追蹤的格式。由於缺少 vmlinux
(未壓縮的核心映像檔案),BTF
生成被跳過。這不影響模組功能,只是缺少額外的調試資訊。xo-user
。透過 mokutil
命令檢查是否開啟 Secure Boot,由以下得知確實有開啟 Secure Boot。
根據作業的說明,為了方便還是採用方法一。
commit c86c1ce
再 fork 完 kxo 並編譯後,執行 sudo ./xo-user
後發現 Ctrl-Q 無法確實離開對弈,只能按下 Ctrl-C 強制 xo-user
結束。
使用 cat -v
檢查輸入 Ctrl-Q 後是否有正確產生控制字元,可以發現有
輸入 Ctrl-P,Ctrl-Q,Ctrl-C 後,只有出現 ^P^C
,可能是終端機配置中,Ctrl-Q 被綁定為特殊控制字符,導致 Ctrl-Q 不被程式接收。
使用 $ stty -a
檢查終端機設置,看 Ctrl-Q 是被用於什麼地方。
ixon
表示啟用了 START/STOP 輸出控制。 一旦已啟用 START/STOP 輸出控制,可以按 Ctrl-S 來暫停輸出至終端機,並按 Ctrl-Q 來恢復輸出至終端機。所以終端機會攔截並處理 Ctrl-Q 和 Ctrl-S 作為流控制字符,而不是將它們傳遞給應用程式。
在 bits/termios-ciflags.h
中有寫到 ixon
的定義
在執行 xo-user
的終端機中按下:
xo-user
,但 xo-user
還是在背景執行。使用 $ stty -ixon
可以禁用軟體流控制,再用 $ stty ixon
恢復軟體流控制。但為了不手動改變終端機預設條件,我們需要去改變 xo-user.c
中的 raw_mode_enable
函式,來停用 ixon
。
重新編譯 xo-user.c
在執行後,Ctrl-S 也不會停止終端機畫面的刷新,使用 Ctrl-Q 後會中止 kxo
內核模組。
執行以下命令發現 kxo
內核模組還存活著,kxo_state
的 end 也為 1。
但是透過 $ sudo dmesg --follow
會發現沒有執行任何有關 kxo 的函式,即便有執行 $ sudo cat /dev/kxo
,因為核心模組已經中止了。如果要讓 kxo 核心模組重新執行,需要先卸載模組後,再重新載入。
kxo_state
檔案沒有出現換行commit 2829628
當使用 cat
命令獲取 /sys/class/kxo/kxo/kxo_state
的內容時會有以下結果:
可以發現並沒有正確換行,但是在 kxo_state_show
函式中,snprintf
使用了格式字串 "%c %c %c\n"
,確實有用到換行符號 \n
,然而,實際輸出卻沒有換行。
原因是 snprintf
中的第二個參數 size
(原始設為 6),限制了寫入緩衝區的字元數,而這個限制包含了結束符號 \0
,導致換行符號 \n
被截斷,所以實際輸出電成 "%c %c %c\0"
,而不是預期的 "%c %c %c\n\0"
。而修正發法只需要把 6 改成 7,就可以正確輸出換行符號了。
修正後就可以看到終端機有出現換行了:
commit 33f52d9
在 game_tasklet_func
函式裡,每次都會把 drawboard_work
放入 workqueue 當中執行,不管下棋狀態是否為 finish
。而這樣做會導致在終端機畫面中一直看到相同的棋盤狀態,因為只有當 AI 演算法完成計算,決定出當前動作後,棋盤狀態才有出現變化。
然後 AI 演算法的計算時間相當的久,導致 drawboard_work_func
一直在產生相同的棋盤狀態,這不僅浪費 cpu 資源,也導致很難在終端機中觀察出棋盤狀態的變化,因為前後的棋盤狀態可能是相同的。
為了減少 drawboard 的次數,需要移動把 drawboard 的
work item 放到 workqueue 的這項動作,移動到 AI 演算法完成後才執行,這樣就可以確保每次 drawboard_work_func
畫出的棋盤狀態都是有差異的,並且減少 drawboard 的次數。
使用 sudo dmesg --follow
觀察 kernel,會看到以下結果。只有當 ai_work_func completed
,才會出現 drawboard_work_func
這項 work item (每 7 行為一組)。
如果 AI 還沒完成,就不會出現 drawboard_work_func
(每 5 行為一組)。這也驗證了這項修改確實可以減少呼叫 drawboard 的次數。
以肉眼觀察 dmesg --follow
的輸出,有明顯發現 ai_one (MCTS) 的運算時間明顯長於 ai_two (Negamax)。