# 2019q1 Homework2 (fibdrv)
contributed by < `bauuuu1021` >
## 環境及初始測試
* linux version
```shell
$ uname -r
4.15.0-45-generic
```
* 安裝 linux-headers 套件
```shell
$ sudo apt install linux-headers-`uname -r`
```
* 確認 linux-headers 套件已正確安裝
```shell
bauuuu1021@x555:~$ dpkg -L linux-headers-4.15.0-45-generic | grep "/lib/modules"
/lib/modules
/lib/modules/4.15.0-45-generic
/lib/modules/4.15.0-45-generic/build
```
* 使用者身份測試
```shell
$ whoami
bauuuu1021
$ sudo whoami
root
```
## 自我檢查清單
### fibdrv.c 中巨集作用
>檔案 fibdrv.c 裡頭的 MODULE_LICENSE, MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_VERSION 等巨集做了什麼事,可以讓核心知曉呢? [color=#7F8580]
* 編譯完成後執行 `$ modinfo fibdrv.ko` 得到以下輸出
```shell
filename: /home/bauuuu1021/fibdrv/fibdrv.ko
version: 0.1
description: Fibonacci engine driver
author: National Cheng Kung University, Taiwan
license: Dual MIT/GPL
...
```
將 `fibdrv.c` 中
```C=13
MODULE_VERSION("0.1");
```
改為
```C=13
MODULE_VERSION("1.0");
```
執行結果變成
```shell
filename: /home/bauuuu1021/fibdrv/fibdrv.ko
version: 1.0
description: Fibonacci engine driver
author: National Cheng Kung University, Taiwan
license: Dual MIT/GPL
```
>根據結果,這些 macro 很有可能是用來印出 module 相關資訊。但只有猜測不符合科學精神,以下為驗證過程[name=bauuuu1021][color=#0ABAB5]
* 在 [linux/module.h](https://github.com/torvalds/linux/blob/master/include/linux/module.h) 中,可以發現題目要求探討的 macro 都是展開為另一個 macro `MODULE_INFO`,例如:
```C=
#define MODULE_LICENSE(_license) MODULE_INFO(license, _license)
```
而 `MODULE_INFO` 又會再<s>呼叫</s>展開為另一個 macro `__MODULE_INFO`
:::warning
preprocessor 的術語不是「呼叫」,而是「展開」(expand),注意用語,兩者行為截然不同
:notes: jserv
:::
```clike
/* Generic info of form tag = "info" */
#define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info)
```
* `__MODULE_INFO` 是被定義在另一個 header file [linux/moduleparam.h](https://github.com/torvalds/linux/blob/master/include/linux/moduleparam.h)
```C=
#ifdef MODULE
#define __MODULE_INFO(tag, name, info) \
static const char __UNIQUE_ID(name)[] \
__used __attribute__((section(".modinfo"), unused, aligned(1))) \
= __stringify(tag) "=" info
#else /* !MODULE */
/* This struct is here for syntactic coherency, it is not used */
#define __MODULE_INFO(tag, name, info) \
struct __UNIQUE_ID(name) {}
#endif
```
* [\_\_attribute\_\_](https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes)
>The keyword \_\_attribute\__ allows you to specify special attributes of variables or structure fields.This keyword is followed by an attribute specification inside **double parentheses**.
* section(".modinfo")
>Sometimes, however, you need additional sections, or you need certain particular variables to appear in special sections, for example to map to special hardware. The section attribute specifies that a variable (or function) lives in a particular section.
* unused
表示這個變數可能宣告但不被用到,加上 `unused` 可避免 gcc 產生 warning
* aligned(size)
可將變數配置在 `size` 大小的記憶體空間內,方便一些特定指令的操作,如:
* 在 68040 上,指令 `move16` 要求 16-byte aligned operands
* 此時若宣告 `int x __attribute__ ((aligned (16))) = 0;` 即可滿足 16-byte aligned
* [_\_stringify](https://blog.csdn.net/luckywang1103/article/details/73863043)
* 定義在 [linux/stringify.h](https://github.com/torvalds/linux/blob/master/tools/include/linux/stringify.h) 中
```clike
#define __stringify_1(x...) #x
#define __stringify(x...) __stringify_1(x)
```
* 根據同樣 header file 中的註解,這樣的兩層結構是為了 `allows the parameter to be a macro itself`,亦即允許 `__stringify` 傳入之參數為 macro
* [# 及 ## 的用途](https://blog.csdn.net/Richard_LiuJH/article/details/45669207)
| 符號 | 作用 | 舉例 |
| -------- | -------- | -------- |
| # | 表可以轉為 string | 例如 #id 為 "id",id=6 時(使用 `#define`)<br>#id 等於 "6"
| ## | 表可以連接 | __initcall_##fn##id 等於 __initcall_fnid <br>fn = test_init,id = 6 時(使用 `#define`)<br>__initcall_##fn##id 等於 __initcall_test_init6 |
實驗 1 :
```clike
#include <stdio.h>
#define _str(x) #x
#define str(x) _str(x)
#define x 5
int main () {
printf("%d\n", x); // output : 5
printf("%s %s\n", str(x), _str(x)); // output : 5 x
}
```
實驗 2 :
```clike
#include <stdio.h>
struct node {
int x;
char y;
};
#define def(a,b) struct node a##_##b;
int main () {
def(create,node);
create_node.x = 5;
printf("%d\n", create_node.x); // output : 5
}
```
### insmod 對應操作
>insmod 這命令背後,對應 Linux 核心內部有什麼操作呢?請舉出相關 Linux 核心原始碼並解讀 [color=#7F8580]
* [strace](https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/strace.html) 可以追蹤一個系統呼叫之參數、return value、execution time 等。
* 使用 strace 追蹤 insmod 之行為,結果如下
```shell
$ sudo strace insmod fibdrv.ko
execve("/sbin/insmod", ["insmod", "fibdrv.ko"], 0x7fffc8dd46f8 /* 17 vars */) = 0
brk(NULL) = 0x562bf3deb000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=178409, ...}) = 0
mmap(NULL, 178409, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f6b78caa000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\260\34\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2030544, ...}) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f6b78ca8000
mmap(NULL, 4131552, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f6b786be000
mprotect(0x7f6b788a5000, 2097152, PROT_NONE) = 0
mmap(0x7f6b78aa5000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1e7000) = 0x7f6b78aa5000
mmap(0x7f6b78aab000, 15072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f6b78aab000
close(3) = 0
arch_prctl(ARCH_SET_FS, 0x7f6b78ca9540) = 0
mprotect(0x7f6b78aa5000, 16384, PROT_READ) = 0
mprotect(0x562bf36d3000, 8192, PROT_READ) = 0
mprotect(0x7f6b78cd6000, 4096, PROT_READ) = 0
munmap(0x7f6b78caa000, 178409) = 0
brk(NULL) = 0x562bf3deb000
brk(0x562bf3e0c000) = 0x562bf3e0c000
uname({sysname="Linux", nodename="x555", ...}) = 0
openat(AT_FDCWD, "/lib/modules/4.15.0-45-generic/modules.softdep", O_RDONLY|O_CLOEXEC) = 3
fcntl(3, F_GETFL) = 0x8000 (flags O_RDONLY|O_LARGEFILE)
fstat(3, {st_mode=S_IFREG|0644, st_size=540, ...}) = 0
read(3, "# Soft dependencies extracted fr"..., 4096) = 540
read(3, "", 4096) = 0
close(3) = 0
openat(AT_FDCWD, "/proc/cmdline", O_RDONLY|O_CLOEXEC) = 3
read(3, "BOOT_IMAGE=/boot/vmlinuz-4.15.0-"..., 4095) = 119
read(3, "", 3976) = 0
close(3) = 0
getcwd("/home/bauuuu1021/fibdrv", 4096) = 24
stat("/home/bauuuu1021/fibdrv/fibdrv.ko", {st_mode=S_IFREG|0664, st_size=8288, ...}) = 0
openat(AT_FDCWD, "/home/bauuuu1021/fibdrv/fibdrv.ko", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0664, st_size=8288, ...}) = 0
mmap(NULL, 8288, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f6b78cd3000
finit_module(3, "", 0) = 0
munmap(0x7f6b78cd3000, 8288) = 0
close(3) = 0
exit_group(0) = ?
+++ exited with 0 +++
```
* 倒數第五行的 `finit_module` 根據 [man finit_module(2)](https://linux.die.net/man/2/finit_module)
>The finit_module() system call is like init_module(), but reads the module to be loaded from the file descriptor fd
* 而 init_module() 之描述如下
>init_module() loads an ELF image into kernel space, performs any necessary symbol relocations, initializes module parameters to values provided by the caller, and then runs the module's init function
>
>TODO 找出相對應程式碼 [color=red]
>當我們透過 insmod 去載入一個核心模組時,為何 module_init 所設定的函式得以執行呢? Linux 核心做了什麼事呢? [color=#7F8580]
>
* 根據 [kernel documentation](https://www.kernel.org/doc/htmldocs/kernel-hacking/routines-init-again.html)
>**The module_init() macro defines which function is to be called at module insertion time** (if the file is compiled as a module), or at boot time: if the file is not compiled as a module the module_init() macro becomes equivalent to __ initcall(), which through linker magic ensures that the function is called on boot.
>
亦即被 module_init() 當作參數之 function 會在 module 被載入 kernel 時執行
>補充:
>module 有兩種運作方式:
>
> 1. 編譯成可動態鏈結的 module,透過 `insmod` 指令來動態載入及初始化。
> 2. 編譯成靜態與 kernel 鏈結,系統啟動過程中進行初始化。
>
>題目要求為 `insmod` 之行為,因此此處僅對 dynamic linking 做探討
>[name=bauuuu1021][color=#0ABAB5]
### 解讀針對特定應用場景使用 Linux 核心的案例
>這個 `fibdrv` 名稱取自 Fibonacci driver 的簡稱,儘管在這裡顯然是為了展示和教學用途而存在,但針對若干關鍵的應用場景,特別去撰寫 Linux 核心模組,仍有其意義,請找出 Linux 核心的案例並解讀。提示: 可參閱 [Random numbers from CPU execution time jitter](https://lwn.net/Articles/642166/)
>[color=#7F8580]
>
>* [meaning of entropy](https://en.wikipedia.org/wiki/Entropy_(computing))
>In computing, **entropy is the randomness collected by an operating system or application for use in cryptography or other uses that require random data.** This randomness is often collected from hardware sources, either pre-existing ones such as mouse movements or specially provided randomness generators.
>
* 加密隨機性
* 加密隨機性一直是 kernel random number community 中廣泛討論的議題,依賴 timing of user input 或 disk interrupt 的加密常遇到以下問題:這些 event 不一定會發生(或在需要呼叫時發生)
* Stephan Müller 提出一個可預期一定發生的方案:CPU execution time jitter
* 對 hardware-level 沒有特別存取能力的 attacker 無法預測 jitter,使 jitter 成為一個良好的加密來源
* random bit generator
* 透過 `jent_measure_jitter()` 來實現,回傳一個 1-bit 結果
* function 內容為將一段 buffer 內的數值都加一,完成後比較當下的及前一個 timestamp,得到差值 `delta`
* 將 `delta` fold down to a single bit,即為結果
* 值得注意的是 folding operation 固然有較快的操作方式,但根據相關程式碼中的註解:不應該在此執行編譯最佳化(`optimization for the jitter random number generator (RNG) must be turned off`),以避免得到不佳的亂數(`avoid the possibility of getting bad random numbers`)
* 與 kernel 關係
* kernel's deterministic random bit generator (DRBG) is currently initialized by making a call to `get_random_bytes()`.
In certain circumstances that pool **will not have been seeded from enough events to provide the entropy required.**
>註 **currently:**
>以上敘述發表於 April 29, 2015[name=bauuuu1021][color=#0ABAB5]
>
* Müller 的 patch 中建立了一個只能被 kernel 存取的 kernel_pool。藉此避免了 **user-space program 透過不斷讀取 `/dev/random` 來消耗系統產生的 entropy** 這種 DoS 攻擊
## 改善效率
>在 GitHub 上 fork fibdrv,目標是改善 fibdrv 計算 Fibinacci 數列的執行效率,過程中需要量化執行時間,可在 Linux 核心模組和使用層級去測量
> * 在 Linux 核心模組中,可用 ktime 系列的 API
> * 在 userspace 可用 clock_gettime 相關 API
> * 分別用 gnuplot 製圖,分析 Fibonacci 數列在核心計算和傳遞到 userspace 的時間開銷,單位需要用 us 或 ns (自行斟酌)
> * 嘗試解讀上述時間分佈,特別是隨著 Fibonacci 數列增長後,對於 Linux 核心的影響
> * 原本的程式碼只能列出到 Fibonacci(100),請修改程式碼,列出後方更多數值 (注意: 檢查正確性和數值系統的使用)
>
>[color=#7F8580]
:::warning
發現問題:
* 1~92 之 fib number 皆正確,但 93~100 之 fib number 皆為 <br>fib(92) = 7540113804746346429,顯然有問題
* 現在用來儲存 fib number 的 data type 為 `long long int`,而 `LLONG_MAX` = 9223372036854775807($2^{63}-1$) 恰比 fib(92) 略大,故 fib(93) 就超出 `long long int` 可表示的範圍。<br>然而就算將 data type 改為 `unsigned long long`,`ULLONG_MAX` = 18446744073709551615 ($2^{64}-1$) 也會很快不敷使用 (未到達 fib(100) ),因此必須用其他方式解決。
[name=bauuuu1021]
:::
* 參考過去大三上 OS 課程的[作業](https://github.com/bauuuu1021/mailbox),也是需要修改 kernel module 及探討 kernel <-> user 間的溝通。
* 那時是將要從 kernel 傳回 user space 的資料放在 `char *buf` 中
```Clike
strcpy(buf,temp);
```
>不佳,建議使用 `strncpy` [color=#0ABAB5][name=(寫完一年多後的)bauuuu1021]
>
在 user space 再從 `buf` 中取出結果
```Clike
int ret_val = read(sysfs_fd,buf,sizeof(buf));
// buf .....
```
* 將作法移植過來卻發現會導致程式中止
```shell
make[1]: Leaving directory '/home/bauuuu1021/fibdrv'
sudo ./client > out
Killed
Makefile:36: recipe for target 'check' failed
make: *** [check] Error 137
```
* 用 gdb 逐條檢查發現,在
```Clike
sz = read(fd, buf, 8);
```
發生
```
Program terminated with signal SIGKILL, Killed.
```
代表它所對應到 kernel function `fib_read()` 出了問題
* `fib_read()` 中有一行
```Clike
strncpy(buf, (char*)&runtime, strlen((char*)&runtime));
```
拿掉這行 `$ make check` 即正確執行
* 在 fibdrv.c 中無法 include <string.h>,故猜測 string.h 中所定義的函式無法在此檔案中執行
* 驗證:
```Clike
char a[6];
memset(a, 0, sizeof(a));
strncpy(a, "aaaaa", 5);
printk(KER_INFO "%s", a);
```
可正常執行且印出正確字串,猜測錯誤。
* 跟強者我同學討論後,被建議使用 [copy to user](http://huenlil.pixnet.net/blog/post/23507650-%5B%E8%BD%89%5Duser-space-kernel-space-%E7%9A%84io%E8%A7%80%E5%BF%B5%E5%8F%8A%E5%AF%A6%E4%BD%9C),主要原因為在 kernel space 對傳入的 pointer 指向的記憶體空間進行操作,可能造成非預期的結果。詳可見 [johnnylord](https://hackmd.io/s/rkLAuRs8V) 同學的共筆。
* 需 `#include <linux/uaccess.h>`
* [ktime_t](https://github.com/spotify/linux/blob/master/include/linux/ktime.h) 在不同架構(32&64 bits)的電腦上的行為可能不同,在我的實驗環境上 (64 bits) 是被當作 `long long int`,因此測量時間傳回 user space 後要使用 `atoll` 將 buf 內容轉回數值
* 執行結果
* kernel space 及 user space 所需的時間比較圖
![](https://i.imgur.com/gXQk1fX.png)
* kernel space 在整個過程中所佔的時間,以百分比表示
![](https://i.imgur.com/2TvvYD5.png)
* 因此從發 request 到收到結果的過程中,真正花在計算上的時間 (costing time on kernel space) 只有大約 20%
## 參考資料
* [GitHub link](https://github.com/bauuuu1021/fibdrv)
* [作業要求](https://hackmd.io/s/SJ2DKLZ8E)
* [2019q1 HW2 作業區](https://hackmd.io/s/rygjaEK8V)
* [HTML color codes](https://htmlcolorcodes.com/)
* [linux 模組化機制](https://blog.csdn.net/yueqian_scut/article/details/46694229)
* [The first 300 Fibonacci numbers](http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibtable.html)
* [limit of each data type](http://www.cplusplus.com/reference/climits/)
* [Gnuplot](https://hackmd.io/c/rJKbX1pFZ/https%3A%2F%2Fhackmd.io%2Fs%2FSkwp-alOg)
###### tags: `bauuuu1021`, `sysprog`,`2019spring`