# 2024q1 Homework6 (integration)
contributed by < `LindaTing0106` >
## 自我檢查清單
## 閱讀〈[Linux 核心模組運作原理](https://hackmd.io/@sysprog/linux-kernel-module)〉
### MODULE_LICENSE 巨集指定的授權條款核心有什麼影響
:::danger
注意用語:code 作為原始程式碼的語境,翻譯為「程式碼」
:::
需要在<s>代碼</s> 程式碼中加入 MODULE_LICENSE ,否則程式連編譯都無法。
```
modpost: missing MODULE_LICENSE()
```
:::danger
為何有此機制?
:::
根據 [Linux kernel licensing rules](https://docs.kernel.org/process/license-rules.html#id1) ,需要有 MODULE_LICENSE 的目的在於識別此模組為自由軟體亦或是專有軟體。
在 [linux/module.h](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/include/linux/module.h) 裡頭可以找到可定義的 License :
<s>免費</s> 自由軟體為:
- "GPL" [GNU Public License v2]
- "GPL v2" [GNU Public License v2]
- "GPL and additional rights" [GNU Public License v2 rights and more]
- "Dual BSD/GPL" [GNU Public License v2 or BSD license choice]
- "Dual MIT/GPL" [GNU Public License v2 or MIT license choice]
- "Dual MPL/GPL" [GNU Public License v2 or Mozilla license choice]
<s>不是免費</s> 專有軟體:
- "Proprietary" [Non free products]
當一模組被標示為專有軟體,則在讀取此模組時核心會將此模組標示為 P ,並且不允許使用 EXPORT_SYMBOL_GPL() 導出的 symbols 。
:::danger
關鍵不是「免費」,參照第三週教材,從 Revolution OS 紀錄片得知授權條款背後的精神。
:::
:::info
根據第三週教材, free software 指的是有散播原始程式碼的自由。確保你可取得原始碼,確保你可以修改該軟體並使用它為基礎創造新的自由軟體;以及確保你認知到你擁有這些權力。
:::
### insmod 後,Linux 核心模組的符號 (symbol) 如何被 Linux 核心找到
在程式中有 `module_init(init_fib_dev)` ,而此處 gcc 在編譯後會將我們傳進去的參數設為 `int init_module(void)` 的別名。
`module_init` 檢查傳入的函式,回傳值是否正確,並把 `init_module` 和傳入的函式關聯起來,因為 `insmod` 指令實作內部會呼叫 `init_module`。如此一來呼叫 `init_module` 就等同於呼叫我們自己寫的函式。
而因為我們在使用 `strace` 追蹤 `insmod` 會做什麼行為時,可以看到其會呼叫 `finit_module` , 而後再進入到 `load_module` ,則在 `load_module` 內部程式碼中可以看到在執行 `do_init_module` 之前,會先做 `layout_and_allocate` 為載入的 module 進行記憶體配置,而進入 `do_init_module` 後,將再執行 `do_one_initcall` ,而 `init_function` 就在 `do_one_initcall` 被執行到。
:::danger
你如何確認?用第七週介紹的 UML 和 QEMU 來追蹤。
:::
:::info
根據 [建構 User-Mode Linux 的實驗環境](https://hackmd.io/@sysprog/user-mode-linux-env) ,在 gdb 設好幾個中斷點後,將 `hello.ko` 掛載後顯示以下訊息。
```
Thread 1 "vmlinux" hit Breakpoint 1, load_module (info=info@entry=0x609efdd8, uargs=uargs@entry=0x40097ca0 <error: Cannot access memory at address 0x40097ca0>, flags=flags@entry=0) at kernel/module.c:3875
3875 {
(gdb)
Continuing.
Thread 1 "vmlinux" hit Breakpoint 2, layout_and_allocate (flags=0, info=0x609efdd8) at kernel/module.c:3488
3488 err = check_modinfo(info->mod, info, flags);
(gdb)
Continuing.
Thread 1 "vmlinux" hit Breakpoint 3, layout_and_allocate (flags=<optimized out>, info=0x609efdd8) at kernel/module.c:3540
3540 return mod;
(gdb) p mod
$17 = (struct module *) 0x64822140
(gdb) c
Continuing.
Thread 1 "vmlinux" hit Breakpoint 6, load_module (info=info@entry=0x609efdd8, uargs=uargs@entry=0x40097ca0 <error: Cannot access memory at address 0x40097ca0>, flags=flags@entry=0) at kernel/module.c:4050
4050 return do_init_module(mod);
(gdb)
Continuing.
Thread 1 "vmlinux" hit Breakpoint 7, do_init_module (mod=mod@entry=0x64822140) at kernel/module.c:3635
3635 {
(gdb) p mod
$18 = (struct module *) 0x64822140
(gdb) c
Continuing.
Thread 1 "vmlinux" hit Breakpoint 8, do_one_initcall (fn=0x64822000) at init/main.c:1217
1217 {
(gdb) c
Continuing.
Thread 1 "vmlinux" hit Breakpoint 11, do_one_initcall (fn=0x64822000) at init/main.c:1226
1226 ret = fn();
(gdb) n
Hello World! - init
```
在進入 `do_init_module` 前 `load_module` 會先設置好變數 `mod` 的值,而在進入 `layout_and_allocate` 配置此模組在記憶體中的位置,而後 mod 的值已經為 `0x64822140` 了,而在 `do_init_module` 中會將 `mod->init` 也就是 `0x64822000` 傳入 `do_one_initcall` 中,而後在此函式中去呼叫 `fn()` ,則顯示出 `Hello World! - init` 。
:::
:::warning
- 沒有看到 mod->init 是在哪裡被賦予值。
:::
### [strace](https://man7.org/linux/man-pages/man1/strace.1.html) 追蹤 Linux 核心的掛載,涉及哪些些系統呼叫和子系統?
<s>
- execve() :執行呼叫的程式。
- mmap() :把文件內容映射到一段虛擬記憶體上,通過對這段記憶體的讀取和修改,實現對文件的讀取和修改。
- openat() :當傳給函數的路徑名是絕對路徑時, open 與 openat 無區別。
</s>
:::danger
注意用語:
* file 是「檔案」,而非「文件」(document)
* 研讀第一手材料,避免低劣的簡體中文翻譯文章
:::
## 閱讀《The Linux Kernel Module Programming Guide》(LKMPG)
### 解釋 simrupt 程式碼裡頭的 mutex lock 的使用方式
simrupt 是由 simulate 和 interrupt 二個單字組合而來。
在資料傳入使用者層級之前,資料會先儲存在 `rx_fifo` 中。
simrupt 中設了三個 lock :分別為 read_lock 、 producer_lock 和 consumer_lock 。而每次做讀取時,當前執行緒就會先將 read_lock 鎖起來,防止別的執行緒重複做到以下程式片段。
```c
do {
ret = kfifo_to_user(&rx_fifo, buf, count, &read);
if (unlikely(ret < 0))
break;
if (read)
break;
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
ret = wait_event_interruptible(rx_wait, kfifo_len(&rx_fifo));
} while (ret == 0);
pr_debug("simrupt: %s: out %u/%u bytes\n", __func__, read,
kfifo_len(&rx_fifo));
```
而在 `simrupt_work_func` 中則使用到了 `producer_lock` 和 `consumer_lock` 在這邊 `consumer_lock` 在取 `circular buffer` 前會先鎖住 `consumer_lock` ,以防同時有別的執行緒對 `circular buffer` 進行存取,而在當前執行緒取得值後,便將 `consumer_lock` 解鎖,之後程式繼續運行,而同樣在值要放入 `kfifo buffer` 時,會將 `producer_lock` 上鎖,而在存入值後便將 `producer_lock` <s>鬆開</s> 解鎖。這裡由於從 `circular buffer` 存取值和將值放入 `kfifo buffer` 並不衝突,故 `consumer_lock` 和 `producer_lock` 不會互相影響。
:::danger
什麼「鬆開」?使用精準的詞彙。
不要「舉燭」,你能從實驗確認你對程式碼的認知嗎?
:::
## 探討 Timsort, Pattern Defeating Quicksort (pdqsort) 及 Linux 核心 [lib/sort.c](https://github.com/torvalds/linux/blob/master/lib/sort.c) 在排序過程中的平均比較次數
在 [fluxsort](https://github.com/scandum/fluxsort) 中可以看到作者是使用 `bench.c` 檔去觀察不同排序演算法的各種比較,利用原作者提供的檔案我們可以測試 timsort 和 pdqsort 的效能。
這是執行出來的結果。
```
| Name | Items | Type | Best | Average | Compares | Samples | Distribution |
| --------- | -------- | ---- | -------- | -------- | --------- | ------- | ---------------- |
| timsort | 100000 | 32 | 0.008617 | 0.008822 | 1532560 | 10 | random order |
| pdqsort | 100000 | 32 | 0.006241 | 0.006335 | 1875709 | 10 | random order |
| | | | | | | | |
| timsort | 100000 | 32 | 0.005844 | 0.005937 | 1050602 | 10 | random % 100 |
| pdqsort | 100000 | 32 | 0.002654 | 0.002724 | 815320 | 10 | random % 100 |
| | | | | | | | |
| timsort | 100000 | 32 | 0.000144 | 0.000148 | 99999 | 10 | ascending order |
| pdqsort | 100000 | 32 | 0.000299 | 0.000300 | 200010 | 10 | ascending order |
| | | | | | | | |
| timsort | 100000 | 32 | 0.001104 | 0.001175 | 299994 | 10 | ascending saw |
| pdqsort | 100000 | 32 | 0.006847 | 0.006915 | 3082227 | 10 | ascending saw |
| | | | | | | | |
| timsort | 100000 | 32 | 0.000647 | 0.000671 | 200001 | 10 | pipe organ |
| pdqsort | 100000 | 32 | 0.004417 | 0.004517 | 2472304 | 10 | pipe organ |
| | | | | | | | |
| timsort | 100000 | 32 | 0.000145 | 0.000154 | 99999 | 10 | descending order |
| pdqsort | 100000 | 32 | 0.000429 | 0.000441 | 300032 | 10 | descending order |
| | | | | | | | |
| timsort | 100000 | 32 | 0.001172 | 0.001186 | 300010 | 10 | descending saw |
| pdqsort | 100000 | 32 | 0.007551 | 0.007720 | 3219752 | 10 | descending saw |
| | | | | | | | |
| timsort | 100000 | 32 | 0.002413 | 0.002447 | 508111 | 10 | random tail |
| pdqsort | 100000 | 32 | 0.004687 | 0.004765 | 1850433 | 10 | random tail |
| | | | | | | | |
| timsort | 100000 | 32 | 0.004718 | 0.004807 | 866402 | 10 | random half |
| pdqsort | 100000 | 32 | 0.005524 | 0.005677 | 1953722 | 10 | random half |
| | | | | | | | |
| timsort | 100000 | 32 | 0.001120 | 0.001140 | 461648 | 10 | ascending tiles |
| pdqsort | 100000 | 32 | 0.003520 | 0.003560 | 1713633 | 10 | ascending tiles |
| | | | | | | | |
| timsort | 100000 | 32 | 0.004282 | 0.004352 | 1532027 | 10 | bit reversal |
| pdqsort | 100000 | 32 | 0.005813 | 0.005890 | 1828342 | 10 | bit reversal |
```
原本在執行時, pdqsort 的比較次數皆為 0 ,看了程式之後發現是因為沒有將程式中設定的 `cmp` 傳入 pdqsort 函式中,也就造成了在比較時,不會去計算比較次數。但改為程式中設定的 `cmp` ,也導致其執行時間變慢。
在執行程式時,若是執行由 c++ 撰寫的演算法,<s>且當前需排序資料不為 4 bytes 則排序會出錯。</s> 並且資料型態不為 32 位元時會出錯。
:::danger
改進你的漢語表達。
:::