---
# System prepended metadata

title: 2021 LDD3 第2章
tags: [Linux Device Drivers, 'Linux ']

---

2021 LDD3 第2章
===

contribute by `Liao712148`
###### tags: `Linux Device Drivers`, `Linux `


### 遇到的問題
**ERROR: could not insert module hello.ko: Operation not permitted**
解決辦法：
根據
[insmod: ERROR: could not insert module HelloWorld.ko: Operation not permitted](https://stackoverflow.com/questions/58546126/insmod-error-could-not-insert-module-helloworld-ko-operation-not-permitted)
會使用到
[Why do I get “Required key not available” when install 3rd party kernel modules or after a kernel upgrade?](https://askubuntu.com/questions/762254/why-do-i-get-required-key-not-available-when-install-3rd-party-kernel-modules)

根據
>sudo apt install mokutil
sudo mokutil --disable-validation

之後reboot 

---
Makefile 是一種腳本，用來生成模組，
## Makefile

```cpp=

# To build modules outside of the kernel tree, we run "make"
# in the kernel source tree; the Makefile these then includes this
# Makefile once again.
# This conditional selects whether we are being included from the
# kernel Makefile or not.

ifneq ($(KERNELRELEASE),)
# call from kernel build system

obj-m	:= hello.o

else

KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD       := $(shell pwd)

modules:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) modules

endif



clean:
	rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions *.mod modules.order *.symvers *.dwo *.mod.dwo

```

*    第8行：我們寫的Makefile第一次被執行，此時KERNELRELEASE還沒被定義，因此條件不成立。執行else的內容。
*    第15行：因為`?=`表示如果左邊的變數上未定義，就將右邊賦值給左邊。否則維持原來的值。而`/lib/modules/$(shell uname -r)/build`是核心原碼樹的頂層目錄。![](https://i.imgur.com/8j6CmNn.png)，會有一個Makefile幫助我們完成模組。
*    第16行：PWD變數紀錄當的Makefile所在的位子。
*    第18,19行：一開始會切換目錄到`-C`選項所指的目錄（核心原碼樹的頂層目錄），在該處找出核心原碼樹頂層目錄的Makefile。而`M=`選項會使得該Makefile回到模組的原始程式目錄，然後才開始建構第18行所定義的`modules`目標，此目標代表`obj-m`變數所列的模組，也就是`hello.o`。執行第19行時，會造成我們所寫的Makefile又再被執行了一次，此次條件成立，因此會執行第11行。
*    第11行：它宣稱有一個模組要從目的碼`hello.o`建構出來，所產生的模組檔是`hello.ko`。與傳統的Makefile不同，使用了GNU make的擴充語法，樣讓這個Makefile有作用，必須從核心建構系統來調用它 ==> ==利用核心的Makefile來產生模組==。
*    第25行：清除檔案。



```cpp=
#include <linux/init.h>//定義了模組所需的許多函式與變數
#include <linux/module.h>//模組的初始函式與清理函式
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void) {
    printk(KERN_ALERT "Hello, world\n");
    return 0;
}
static void hello_exit(void) {
    printk(KERN_ALERT "Goodbye, cruel world\n");
}
module_init(hello_init);
module_exit(hello_exit);
```
*    第1,2行：定義於`/usr/src/linux-headers-5.11.0-27-generic/include/linux`。
*    第3行：用來讓kernel知道此模組遵守自由授權條款。
*    第11行：在模組載入kernel時會執行module_init巨集中的函式。也就是helo_init。初始函式是向核心註冊模組所提供新功能（可能是整個驅動程式，或是可供應用程式存取的軟體抽象層）。可以將此函式改成`static int __int hello_init()`當中的`__init`讓核心可以知道該函式==只用於初始期間==。
*    第12行：在模組卸載時會執行module_exit巨集中的函式。也就是helo_exit。清理函式負責在模組被卸載之前，註銷曾這冊的軟體介面，並將資源還給系統。以將此函式改成`static void __exit hello_exit()`當中的`__exit`讓核心可以知道該函式==只用於卸載期間==。
*    第5行：使用的是`printk`而不是`printf`，因為`printf`屬於libc，==模組不能與一般函式庫連結，只能與核心連結==，因此要使用定義於linux核心內的`printk`，可供模組使用。而當中的`KERN_ALERT`代表訊息的優先度，我們刻意指定一個高的優先度，因為預設的優先度不一定會使得訊息出現在你看得到的地方。
---



```cpp=
#include <linux/init.h>
#include <linux/module.h>
#include <linux/sched.h>//只要引入sched.h就可以將current當成當時執行的process。
#include <linux/moduleparam.h>
MODULE_LICENSE("Dual BDE/GPL");
static char *whom = "world";
static int howmany = 1;
module_param(howmany, int, S_IRUGO);
module_param(whom, charp, S_IRUGO);
static int hello_init(void) {
    printk(KERN_ALERT "Hello world\n");
    printk(KERN_ALERT "The init process is \"%s\" (pid %i)\n", current->comm, current->pid);
    int i;
    for(i = 0; i < howmany; i++) {
	    printk(KERN_ALERT "%s %d times\n", whom, howmany);
    }
    return 0;
}
static void hello_exit(void) {
    printk(KERN_ALERT "Goodbye, curel world\n");
    printk(KERN_ALERT "The exit process is \"%s\" (pid %i)\n", current->comm, current->pid);
}

module_init(hello_init);
module_exit(hello_exit);

```
*    第3, 11, 21行：核心的大部份動作，都是在代替當時正在執行的process執行遠本無法在user-space完成的工作。透過定義於`<asm/current.h>`的全局變數`current`，current永遠指向當時正在執行的process。透過current，核心程式可以取得當時正在執行的狀態資訊。
*    第6，7，8，9行：在`insmod`時改變系統的參數，模組本身要先以`module_param`巨集來宣告參數。

>`#` insmod hello.ko howmany=10 whom="Mom"

==所有的模組參數都必須有預設值==，`module_param()`的最後一欄是權限值，決定模組參數在sysfs裡的對應項目的存取權限。



### 參考資料
[Makefile 語法和示範](https://hackmd.io/@sysprog/SySTMXPvl)
[編譯內核模塊的的Makefile的講解](https://www.twblogs.net/a/5b81bc442b71772165ae0321)
[驅動模組Makefile解析](https://www.itread01.com/p/1377069.html)