# 2019q1 Homework2 (fibdrv) > contributed by < ```shihyuuuuuuu``` > > [作業要求](https://hackmd.io/s/SJ2DKLZ8E) ## 實驗環境 ``` $ uname -a Linux shihyu 4.18.0-16-generic #17~18.04.1-Ubuntu SMP Tue Feb 12 13:35:51 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux ``` ## 核心模組相關 - 副檔名為 .ko - insmod: 將模組載入到核心中 - rmmod: 將模組從核心移除 - modinfo: 列出該模組的相關資訊(例如: license, description, author...) - lsmod: 列出已經載入到核心中的模組,會顯示模組名稱( Module )、模組的大小( size )、相依性( Used by ) 參考:[鳥哥](http://linux.vbird.org/linux_basic/0510osloader.php#kernel) ## 自我檢查清單 - **檔案 `fibdrv.c` 裡頭的 `MODULE_LICENSE`, `MODULE_AUTHOR`, `MODULE_DESCRIPTION`, `MODULE_VERSION` 等巨集做了什麼事,可以讓核心知曉呢?** 在 linux 原始碼 [/include/linux/module.h](https://elixir.bootlin.com/linux/v4.18/source/include/linux/module.h) 中搜尋`MODULE_LICENSE` 、`MODULE_AUTHOR` 、 `MODULE_DESCRIPTION` 、 `MODULE_VERSION` 等巨集,可以找到以下四巨集: ```clike=199 #define MODULE_LICENSE(_license) MODULE_INFO(license, _license) ``` ```clike=205 #define MODULE_AUTHOR(_author) MODULE_INFO(author, _author) ``` ```clike==208 #define MODULE_DESCRIPTION(_description) MODULE_INFO(description, _description) ``` ```clike=237 #define MODULE_VERSION(_version) MODULE_INFO(version, _version) ``` 可以發現這些巨集的參數都被傳到[同檔案](https://elixir.bootlin.com/linux/v4.18/source/include/linux/module.h)中的一個 `MODULE_INFO` 巨集: ```clike=161 #define MODULE_INFO(tag, info) __MODULE_INFO(tag, tag, info) ``` 再繼續找下去,可以在 [/include/linux/moduleparam.h](https://elixir.bootlin.com/linux/v4.18/source/include/linux/moduleparam.h#L21) 中找到巨集 ```__MODULE_INFO``` 的定義: ```clike=21 #define __MODULE_INFO(tag, name, info) \ static const char __UNIQUE_ID(name)[] \ __used __attribute__((section(".modinfo"), unused, aligned(1))) \ = __stringify(tag) "=" info ``` 一開始看不太懂這段在做什麼,參閱一些資料後才了解,在 .ko 檔案中有一個 '.modinfo' 區段是用來存放模組的資訊(也就是使用 ```modinfo``` 指令會顯示的資訊),而在程式碼中,我們就可以透過 '```MODULE_INFO```' 來加入這些資訊。 '```MODULE_INFO```' 會定義一個像是字典的 key-value 資料在 '.modinfo' 中,猜測是透過程式碼中的 ```__stringify(tag) "=" info``` 來達到。 **參考自:[Anatomy of the Linux loadable kernel module](http://terenceli.github.io/%E6%8A%80%E6%9C%AF/2018/06/02/linux-loadable-module)** 原文:*".ko file will also contain a ‘.modinfo’ section which stores some of the module information. modinfo program can show these info. In the source code, one can **use ‘MODULE_INFO’ to add this information**." "MODULE_INFO just **define a key-value data in ‘.modinfo’ section** once the MODULE is defined."* :::info **```__attribute__``` 用法:** ```__attribute__``` 可以對變量或結構等設定一些特別屬性,依據設定的對象可以分為三類: 1. 函數屬性( Function Attributes ) 2. 變量屬性( Variable Attributes ) 3. 類型屬性( Type Attributes ) 語法:```__attribute__ ((attribute-list))``` 若 attribute-list 有多個屬性則以逗號分隔。 而我認為在這邊是變量屬性,也就是對 ```__UNIQUE_ID(name) ``` 這個字元陣列做設定。 這邊設了三個屬性: 1. section(".modinfo"): 將此變量設定在特定區間中(在這邊為 ".modinfo" ) 2. unused: 表示該變量可能未被使用 3. aligned(1): 設定變量的最小對齊格式為1 byte **參考資料** [5.31 Specifying Attributes of Variables](https://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes) [[轉]GNU C __attribute__ 機制簡介](http://huenlil.pixnet.net/blog/post/26078382-%5B%E8%BD%89%5Dgnu-c-__attribute__-%E6%A9%9F%E5%88%B6%E7%B0%A1%E4%BB%8B) [Setting the license for modules in the linux kernel](https://stackoverflow.com/questions/11927590/setting-the-license-for-modules-in-the-linux-kernel) ::: - **`insmod` 這命令背後,對應 Linux 核心內部有什麼操作呢?請舉出相關 Linux 核心原始碼並解讀** [insmod(8) - Linux man page](https://linux.die.net/man/8/insmod): simple program to insert a module into the Linux Kernel ```insmod``` 這支程式功能是將一個模組嵌入 Linux 核心中,而在[這篇討論](https://www.quora.com/What-does-insmod-internally-do)中,有人說到 insmod 會調用 ```init_module``` 這個系統呼叫。 實際查看 [init_module 的 Man Page](https://linux.die.net/man/2/init_module),內有 ```init_module``` 和 ```finit_module```,兩者的功能都是 load a kernel module ,差別在於前者的參數是指向 "a buffer containing the binary image to be loaded", 而後者是指向一個 file descriptor 。 接著進入 Linux 原始碼中找到在 [kernel/module.c](https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c#L3835) 中 ```init_module``` 的實做: ```clike=3835 SYSCALL_DEFINE3(init_module, void __user *, umod, unsigned long, len, const char __user *, uargs) { int err; ... return load_module(&info, uargs, 0); } ``` 再追蹤最後 return 的 ```load_module```,在 [/kernel/module.c](https://elixir.bootlin.com/linux/v4.18/source/kernel/module.c#L3654) 中,註解顯示它的功能大致上是為模組分配空間並載入: ```cpp= /* Allocate and load the module: note that size of section 0 is always zero, and we rely on this for optional sections. */ static int load_module(struct load_info *info, const char __user *uargs, int flags){ ... } ``` :::success **補充1** 在 [`johnnylord`](https://hackmd.io/s/rkLAuRs8V) 同學的開發紀錄中看到,使用 `strace` 可以紀錄某個process在執行時所產生的系統呼叫。實際執行結果如下: ```shell= $ sudo strace insmod fibdrv.ko [sudo] password for shihyu: execve("/sbin/insmod", ["insmod", "fibdrv.ko"], 0x7ffcc5ede198 /* 27 vars */) = 0 brk(NULL) = 0x564e6e73e000 ... mmap(NULL, 8288, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f22ead02000 finit_module(3, "", 0) = 0 munmap(0x7f22ead02000, 8288) = 0 close(3) = 0 exit_group(0) = ? +++ exited with 0 +++ ``` 確實看到了它有執行 `finit_module` 這個 system call 。 **補充2** 在 Github 上查看 `insmod` 的 [原始碼](https://github.com/vadmium/module-init-tools/blob/master/insmod.c),在第159行有看到 init_module 被呼叫。 ```clike=159 ret = init_module(file, len, options); ``` ::: --- - **當我們透過 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](https://lwn.net/Articles/642166/)** --- - **查閱 [ktime 相關的 API](https://www.kernel.org/doc/html/latest/core-api/timekeeping.html),並找出使用案例 (需要有核心模組和簡化的程式碼來解說)** --- - **[clock_gettime](https://linux.die.net/man/2/clock_gettime) 和 [High Resolution TImers (HRT)](https://elinux.org/High_Resolution_Timers) 的關聯為何?請參閱 POSIX 文件並搭配程式碼解說** - **`fibdrv` 如何透過 [Linux Virtual File System](https://www.win.tue.nl/~aeb/linux/lk/lk-8.html) 介面,讓計算出來的 Fibonacci 數列得以讓 userspace (使用者層級) 程式 (本例就是 `client.c` 程式) 得以存取呢?解釋原理,並撰寫或找出相似的 Linux 核心模組範例** - **注意到 `fibdrv.c` 存在著 `DEFINE_MUTEX`, `mutex_trylock`, `mutex_init`, `mutex_unlock`, `mutex_destroy` 等字樣,什麼場景中會需要呢?撰寫多執行緒的 userspace 程式來測試,觀察 Linux 核心模組若沒用到 mutex,到底會發生什麼問題** - **許多現代處理器提供了 [clz / ctz](v) 一類的指令,你知道如何透過演算法的調整,去加速 [費氏數列](https://hackmd.io/s/BJPZlyDSV) 運算嗎?請列出關鍵程式碼並解說**