# 2024q1 Homework6 (integration)
contributed by < `lintin528` >
## 自我檢查清單
- [ ] 研讀前述 Linux 效能分析 描述,在自己的實體電腦運作 GNU/Linux,做好必要的設定和準備工作
### 實驗環境
```bash
$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
Address sizes: 39 bits physical, 48 bits virtual
CPU(s): 12
On-line CPU(s) list: 0-11
Thread(s) per core: 2
Core(s) per socket: 6
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 151
Model name: 12th Gen Intel(R) Core(TM) i5-12400
Stepping: 2
CPU MHz: 2500.000
CPU max MHz: 4400.0000
CPU min MHz: 800.0000
BogoMIPS: 4992.00
Virtualization: VT-x
L1d cache: 288 KiB
L1i cache: 192 KiB
L2 cache: 7.5 MiB
L3 cache: 18 MiB
NUMA node0 CPU(s): 0-11
$ uname -r
5.15.0-101-generic
$ dpkg -L linux-headers-5.15.0-101-generic | grep "/lib/modules"
/lib/modules
/lib/modules/5.15.0-101-generic
/lib/modules/5.15.0-101-generic/build
```
- [ ] 閱讀〈Linux 核心模組運作原理〉並對照 Linux 核心原始程式碼 (v6.1+),解釋 insmod 後,Linux 核心模組的符號 (symbol) 如何被 Linux 核心找到 (使用 List API)、MODULE_LICENSE 巨集指定的授權條款又對核心有什麼影響 (GPL 與否對於可用的符號列表有關),以及藉由 strace 追蹤 Linux 核心的掛載,涉及哪些系統呼叫和子系統?
看完這個教材後,剛開始有個粗淺的觀念:核心模組的掛載是為了在不重新編譯 kernel 的情況下加入新的功能。以下寫出各個節點所遇到的問題。
在第一步了解核心模組是如何被編譯的,在當前目錄新增了 `hello.c` 與 `Makefile` ,並且透過指令
```$ make -C /lib/modules/`uname -r`/build M=`pwd` modules ```
完成編譯並產出 `.ko` 檔案,這邊可以看出是透過在 `/lib/modules/`uname -r`/build` 目錄下的 `Makefile` 裡描述編譯操作,但就產出一個疑問,為何我需要建立在當前目錄的 `Makefile` ?
目前想出的解釋只有需要指定目標模組 `obj-m` ,以及可以寫一些自定義的規則,所以才需要在當前目錄建立 `Makefile`。
之後以 `fibdrv` 為例去探討如何在 linux 核心中掛載模組,在看這部分內容時,其實一開始有點茫然,但看完之後可以大約總結為將每一個 `MODULE_XXX` 巨集分別透過 `__UNIQUE_ID` 產生一個獨特的名稱,並且從以下這一段
```c
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
.
.
#define __MODULE_INFO(tag, name, info) \
static const char __UNIQUE_ID(name)[]
```
可以看出來,產出的名稱其實就是 tag 所對應到的標籤,如 `author, license` 等等,並且在之後的 `__stringify(tag) "=" info` ,將其內容轉換為 `"操作 = 參數"` 的這個形式後,透過
`__used __attribute__((section(".modinfo"), unused, aligned(1)))` ,在最後所產出的 ELF 格式 object file 中,將先前所生成的字串寫入對應的 `.modinfo` section。然後去觀察使用 `objdump` 產出的內容資訊
```c
0000 76657273 696f6e3d 302e3100 64657363 version=0.1.desc
0010 72697074 696f6e3d 4669626f 6e616363 ription=Fibonacc
```
在 `.modinfo section` 裡面的其中一小段,第一段 `0010` 為此段訊息的十六進制 `offset` ,代表該段數據的偏移量,後面的 `76657273` 其實是對應到該段文字的 ASCII 碼 , `76` 對應到 `v` , `65` 對應到 `e` ,以此類推,至此就是編譯過程中是如何透過 `MODULE_XXX` 系列的巨集將 module 的資訊寫入此 object 檔案的過程。
下一部分在分析使用 `insmod` 時,是如何透過系統呼叫將編譯出的 kernel object (.ko) 檔案掛載進 linux 核心中,在最後的總結中可以看出 `insmod` 實際執行的系統呼叫中主要是透過
```c
openat(AT_FDCWD, "/tmp/fibdrv/fibdrv.ko", O_RDONLY|O_CLOEXEC) = 3
finit_module(3, "", 0) = 0
```
去配置記憶體空間以及加入初始化模組函數的起始位置,而細部觀察 `finit_module` 系統呼叫的實作,在最後呼叫 `load_module` ,且實際的記憶體配置就在裡面的 `layout_and_allocate` ,最後執行 `do_init_module` ,在其實作中的 `ret = fn();` 做實際的模組初始化,另外在讀取 `.ko` 檔案後事透過 `do_init_module` 函式實作中的 `ret = do_one_initcall(mod->init);` 去讀取透過 `module_init` 將使用者自己寫的 init funciton ,在此情況透過 `finit_module` 所執行的模組初始化就會更改為使用者自定義的 `init_fib_dev` 。
- [ ] 閱讀《The Linux Kernel Module Programming Guide》(LKMPG) 並解釋 simrupt 程式碼裡頭的 mutex lock 的使用方式,並探討能否改寫為 lock-free;
- [ ] 探討 Timsort, Pattern Defeating Quicksort (pdqsort) 及 Linux 核心 lib/sort.c 在排序過程中的平均比較次數,並提供對應的數學證明;
- [ ] 研讀 CMWQ (Concurrency Managed Workqueue) 文件,對照 simrupt 專案的執行表現,留意到 worker-pools 類型可指定 "Bound" 來分配及限制特定 worker 執行於指定的 CPU,Linux 核心如何做到?CMWQ 關聯的 worker thread 又如何與 CPU 排程器互動?
- [ ] 解釋 xoroshiro128+ 的原理 (對照〈Scrambled Linear Pseudorandom Number Generators〉論文),並利用 ksort 提供的 xoro 核心模組,比較 Linux 核心內建的 /dev/random 及 /dev/urandom 的速度,說明 xoroshiro128+ 是否有速度的優勢?其弱點又是什麼?
- [ ] 解釋 ksort 如何運用 CMWQ 達到並行的排序;