# 2024q1 Homework6 (integration) contributed by < `dockyu` > ## 作業說明的補充 `uname -r` 輸出目前的 kernel的版本號,我的版本號是 6.5.0-25-generic ```bash $ uname -r 6.5.0-25-generic ``` > #### 核心模組 (kernel module) 的用途 > 既然核心檔案都已經包含了硬體偵測與驅動模組,那麼什麼是核心模組啊?要注意的是, 現在的硬體更新速度太快了,如果我的核心比較舊,但我換了新的硬體,那麼,這個核心肯定無法支援! 怎麼辦?重新拿一個新的核心來處理嗎?開玩笑~核心的編譯過程可是很麻煩的~ 所以囉,為了這個緣故,我們的 Linux 很早之前就已經開始使用所謂的模組化設定了! 亦即是將一些不常用的類似驅動程式的咚咚獨立出核心,編譯成為模組,然後, 核心可以在系統正常運作的過程當中載入這個模組到核心的支援。如此一來, 我在不需要更動核心的前提之下,只要編譯出適當的核心模組,並且載入他,呵呵!我的 Linux 就可以使用這個硬體啦!簡單又方便! 那我的模組放在哪裡啊?可惡!怎麼會問這個傻問題呢?當然一定要知道的啦!就是 /lib/modules/$(uname -r)/kernel/ 當中啦! -------- [鳥歌私房菜 第二十四章、Linux 核心編譯與管理](https://linux.vbird.org/linux_basic/centos7/0540kernel.php) 因為我的版本號是 6.5.0-25-generic ,所以 `/lib/modules` 裡會有一個對應的目錄 6.5.0-25-generic 存放 kernel module ,裡面都是被編譯完的內容 ```bash $ ls /lib/modules 5.15.0-102-generic 5.15.0-25-generic 6.5.0-18-generic 6.5.0-21-generic 6.5.0-25-generic ``` /lib/modules 裡的東西都是已經被編譯完的 module ,為了要開發 module 需要有原始碼,下載目標版本的 linux-headers ```bash sudo apt install linux-headers-$(uname -r) sudo apt install linux-headers-6.5.0-25-generic ``` 下面命令查看 linux-headers 裝到哪裡 ```bash $ dpkg -L linux-headers-6.5.0-25-generic | grep "/lib/modules" /lib/modules /lib/modules/6.5.0-25-generic /lib/modules/6.5.0-25-generic/build ``` 發現 build 其實是個軟連接,對應到 /usr/src/linux-headers-6.5.0-25-generic ```bash $ ls -l /lib/modules/6.5.0-25-generic/build lrwxrwxrwx 1 root root 39 二 20 22:19 /lib/modules/6.5.0-25-generic/build -> /usr/src/linux-headers-6.5.0-25-generic ``` 在 /usr/src 可以看到目標版本的目錄,裡面有原始碼 ```bash $ ls /usr/src linux-headers-5.15.0-102 linux-headers-6.5.0-21-generic linux-hwe-6.5-headers-6.5.0-21 python3.10 linux-headers-5.15.0-102-generic linux-headers-6.5.0-25-generic linux-hwe-6.5-headers-6.5.0-25 ``` ### Makefile 解讀 `obj-m` 是一個用於內核構建系統的變數,指定哪些模組需要被編譯為可加載模組(即 `.ko` 檔案) ,這是內核構建系統特有的變數,不是普通 Makefile 中的標準變數 `modules` 是內核構建系統預定義的一個目標(target),用於編譯所有在 obj-m 變數中指定的模組 如果一個模組由多個源文件組成,則應使用相對應的 <module_name>-objs 變量來列出所有構成該模組的源文件 ```Makefile obj-m += sort.o sort-objs := \ sort_mod.o \ sort_impl.o obj-m += xoro.o xoro-objs := \ xoro_mod.o all: $(GIT_HOOKS) user test_xoro $(MAKE) -C $(KDIR) M=$(PWD) modules ``` 上面的 Makefile , obj-m 說要編譯的 modules 有兩個 `sort` 跟 `xoro` , `sort` 模組依賴的檔案由 sort-objs 定義 , xoro 模組依賴的檔案由 xoro-objs 定義 , all 任務會編譯 modules(`sort` & `xoro`) --- ##### 模組編譯步驟 以 `sort.ko` 產生的步驟為例 1. 編譯 `sort_mod.c` 為 `sort_mod.o` (可以 import `sort.h`) 2. 編譯 `sort_impl.c` 為 `sort_impl.o` (可以 import `sort.h`) 3. 鏈接 `sort_mod.o` 和 `sort_impl.o` 成 `sort.o` 4. 自動生成 `sort.mod.c` (包含模組的 metadata,ex: 作者姓名等) 並自動編譯為 `sort.mod.o` 5. 用 `sort.o` 以及 `sort.mod.o` 生成 `sort.ko` --- `all` target 的輸出為 ```bash make -C /lib/modules/6.5.0-25-generic/build M=/home/dockyu/project/ksort modules ``` `-C /lib/modules/6.5.0-25-generic/build` : 切換工作目錄,而此目錄其實就是 `/usr/src/linux-headers-6.5.0-25-generic` ,這是為了要讓編譯時使用的是當前版本的頭文件,才能讓編譯完的 module 和當前的 kernel 兼容 `M=/home/dockyu/project/ksort` : 要編譯的檔案要去這裡找,編譯好的 .ko 也要出現在這個目錄 --- ```makefile check: all $(MAKE) insmod # 掛載模組 sudo ./user sudo ./test_xoro $(MAKE) rmmod # 移除模組 ``` 上面的 `check` target 依賴 `all` target 也就是會先編譯兩個模組,並且掛載到核心,執行 user(user.c 編譯出的執行檔) 測試 sort 模組,接著執行 test_xoro(test_xoro.c 編譯出的執行檔) 測試 xoro 模組 ## 理解 sort 模組 ``` /ksort ├── sort_mod.c # 主要模組文件,實現模組的加載和卸載函數 ├── sort_impl.c # 實現排序算法的具體邏輯 ├── sort.h # 定義模組間共享的結構、變數和函數原型 └── Makefile # 用於編譯模組的 Makefile ``` #### sort.h 裡面有一個 workqueue_struct 型別的指標 workqueue ```c #include <linux/types.h> extern struct workqueue_struct *workqueue; ``` workqueue_struct 用來儲存需要等待的工作(例如等待硬體就位),詳情查找使用的核心版本的 <linux/type.h> ```bash $ vim /lib/modules/$(uname -r)/build/include/linux ``` #### sort_mod #### sort_impl ### 測試 嘗試直接使用 [2024q1 第 1 週測驗題](https://hackmd.io/@sysprog/linux2024-quiz1) 中的 timsort 程式碼,發現執行過後的結果並沒有被排序,一開始以為是將 user space 的函式直接放到 kernel space 會出問題,後來發現排序完後會鍊結串列會變短,但是剩下的會是被排序過的 ```bash ==== Testing timsort ==== number of list: 1048596 Sorting number of list: 741547 Elapsed time: 138591 Comparisons: 15254864 List is sorted ```