# 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
```