# 2019q1 Homework2 (fibdrv) contributed by < `yiwei01` > ###### tags: `sysprog2019` ## Linux 核心版本 ```shell $ uname -r 4.15.0-46-generic ``` ## 自我檢查清單 - [ ] 檔案 fibdrv.c 裡頭的 MODULE_LICENSE, MODULE_AUTHOR, MODULE_DESCRIPTION, MODULE_VERSION 等巨集做了什麼事,可以讓核心知曉呢? insmod 這命令背後,對應 Linux 核心內部有什麼操作呢?請舉出相關 Linux 核心原始碼並解讀 在 [/linux/module.h](https://github.com/torvalds/linux/blob/master/include/linux/module.h) 可發現 ```MODULE_LICENSE``` , ```MODULE_AUTHOR``` , ```MODULE_DESCRIPTION``` 這些 Macro 又展開了 ```MODULE_INFO``` 這個 Macro ```clike #define MODULE_LICENSE(_license) MODULE_INFO(license, _license) /* * Author(s), use "Name <email>" or just "Name", for multiple * authors use multiple MODULE_AUTHOR() statements/lines. */ #define MODULE_AUTHOR(_author) MODULE_INFO(author, _author) /* What your module does. */ #define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description) ``` 而 ```MODULE_INFO``` 的定義如下 ```clike= /* Generic info of form tag = "info" */ #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info) ``` 接著再到 [linux/moduleparam.h](https://github.com/torvalds/linux/blob/master/include/linux/moduleparam.h) 可發現 ```__MODULE_INFO``` 的定義 ```clike= #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 ``` 一一剖析程式碼: #### static const char __UNIQUE_ID(name)[] 在 [include/linux/compiler-clang.h](https://github.com/torvalds/linux/blob/bb617b9b4519b0cef939c9c8e9c41470749f0d51/include/linux/compiler-clang.h) 中可找到 ```__UNIQUE_ID``` 的定義 ```clike= #define __UNIQUE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) ``` 再到 [include/linux/compiler_types.h](https://github.com/torvalds/linux/blob/9105b8aa50c182371533fc97db64fc8f26f051b3/include/linux/compiler_types.h) 可知 ```__PASTE``` 可以 concatenate Macro 的兩個 arguments (詳見下方 ```__stringify```) ```clike= #define ___PASTE(a,b) a##b #define __PASTE(a,b) ___PASTE(a,b) ``` #### __used \_\_attribute\_\_((section(".modinfo"), unused, aligned(1))) 參考 [Specifying Attributes of Variables](https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes) 可知, #### __stringify(tag) 在 [linux/stringify.h](https://github.com/torvalds/linux/blob/master/tools/include/linux/stringify.h) 中可找到定義 ```clike /* SPDX-License-Identifier: GPL-2.0 */ #ifndef __LINUX_STRINGIFY_H #define __LINUX_STRINGIFY_H /* Indirect stringification. Doing two levels allows the parameter to be a * macro itself. For example, compile with -DFOO=bar, __stringify(FOO) * converts to "bar". */ #define __stringify_1(x...) #x #define __stringify(x...) __stringify_1(x) #endif /* !__LINUX_STRINGIFY_H */ ``` 在分析 Linux 原始碼之前,先透過一些簡單的程式碼確認 ```#``` 和 ```##``` 的功能 程式碼一: ```clike #include <stdio.h> #define str(s) #s #define concat(a,b) a##b int main() { int apple = 5; printf("Hello "str(World)"\n"); // 形同 printf("Hello ""World""\n"); printf("%d\n", concat(app,le)); // 形同 printf("%d\n",apple); return 0; } ``` 功能: 1.使用 ```#``` 把 Macro 的 argument 變為一個字串 2.使用 ```##``` concatenate Macro 的兩個 arguments 程式碼二: ```clike= #include<stdio.h> #define five 5 #define _str(s) #s #define str(s) _str(s) int main() { printf("%d\n",five); // 5 printf(str(five)"\n"); // 5 printf(_str(five)"\n"); // five return 0; } ``` 原理: 只要是 Macro 的定義裡有用 ```#``` 或 ```##``` , 則 Macro 的 auguments 並不會再展開,因此利用第一層的 Macro 先把 Arguments 展開,再展開第二個 Macro,便可以將 Macro 也作為 Arguments 。這也是為什麼在 [linux/stringify.h](https://github.com/torvalds/linux/blob/master/tools/include/linux/stringify.h) 中註解寫道 : " Doing two levels allows the parameter to be a macro itself. " reference : [MODULE_AUTHOR巨集(一)](https://www.itread01.com/content/1541866804.html) - [ ] 當我們透過 insmod 去載入一個核心模組時,為何 module_init 所設定的函式得以執行呢?Linux 核心做了什麼事呢? - [ ] 試著執行 $ readelf -a fibdrv.ko, 觀察裡頭的資訊和原始程式碼及 modinfo 的關聯,搭配上述提問,解釋像 fibdrv.ko 這樣的 ELF 執行檔案是如何「植入」到 Linux 核心 - [ ] 這個 fibdrv 名稱取自 Fibonacci driver 的簡稱,儘管在這裡顯然是為了展示和教學用途而存在,但針對若干關鍵的應用場景,特別去撰寫 Linux 核心模組,仍有其意義,請找出 Linux 核心的案例並解讀。提示: 可參閱 Random numbers from CPU execution time jitter - [ ] 查閱 ktime 相關的 API,並找出使用案例 (需要有核心模組和簡化的程式碼來解說) - [ ] clock_gettime 和 High Resolution TImers (HRT) 的關聯為何?請參閱 POSIX 文件並搭配程式碼解說 - [ ] fibdrv 如何透過 Linux Virtual File System 介面,讓計算出來的 Fibonacci 數列得以讓 userspace (使用者層級) 程式 (本例就是 client.c 程式) 得以存取呢?解釋原理,並撰寫或找出相似的 Linux 核心模組範例 - [ ] 注意到 fibdrv.c 存在著 DEFINE_MUTEX, mutex_trylock, mutex_init, mutex_unlock, mutex_destroy 等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題 - [ ] 許多現代處理器提供了 clz / ctz 一類的指令,你知道如何透過演算法的調整,去加速 費氏數列 運算嗎?請列出關鍵程式碼並解說