【linux】驅動-2-內核模組
- 2021 年 3 月 17 日
- 筆記
- /label/linux, /label/linux/driver, /label/linux/driver/02, /label/lzm, linux, 書籍筆記, 嵌入式, 教程集合
前言
- 以野火i.M 6U為例
2. 內核模組
本章節筆記主要理解內核模組程式碼框架和原理,分析一個簡單的內核模組例子。
需要明確的是模組和驅動是兩回事。
2.1 內核模組概念
2.1.1 內核
內核,是一個作業系統的核心。是基於硬體的第一層軟體擴充,提供作業系統的最基本的功能, 是作業系統工作的基礎,決定著整個作業系統的性能和穩定性。
內核按照體系結構分為:微內核和宏內核。
- 參考圖:
2.1.2 內核模組機制的引入
提高系統靈活性,在調試驅動的時候不需要重新編譯內核,也不需要重新啟動內核,只需要插入需要調試的驅動即可。
內核模組的特點:
- 模組本身不被編譯入內核映像,這控制了內核的大小。
- 模組一旦被載入,它就和內核中的其它部分完全一樣。
2.2 內核模組
內核模組編譯後會得到一個 .ko 的 ELF 文件。(ELF 文件可以百度一下,也可以參考野火的內核模組章節。)
ELF 文件:這類文件包含了程式碼和數據,可以被用來鏈接成可執行文件或共享目標文件,靜態鏈接庫也可以歸為這一類。
2.2.1 內核模組參考常式
必須內容可分為以下幾點:
- 入口函數:當通過insmod或modprobe命令載入內核模組時,模組的載入函數就會自動被內核執行,完成本模組相關的初始化工作。
- 出口函數:執行rmmod命令卸載模組時,模組卸載函數就會自動被內核自動執行,完成相關清理工作。
- 協議:許可證聲明描述內核模組的許可許可權,如果模組不聲明,模組被載入時,將會有內核被污染的警告。
非必須內容:
- 模組參數:模組參數是模組被載入時,可以傳值給模組中的參數。
- 模組導出符號: 模組可以導出準備好的變數或函數作為符號,以便其他內核模組調用。
- 模組的其他相關資訊: 可以聲明模組作者等資訊。
hello_module.c
/** @file hello_module.c
* @brief 簡要說明
* @details 詳細說明
* @author lzm
* @date 2021-02-21 18:08:07
* @version v1.0
* @copyright Copyright By lizhuming, All Rights Reserved
*
**********************************************************
* @LOG 修改日誌:
**********************************************************
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
// 入口函數:安裝驅動時調用的函數
static int __init hello_init(void)
{
printk(KERN_EMERG "[ KERN_EMERG ] Hello Module Init\n");
printk( "[ default ] Hello Module Init\n");
return 0;
}
// 出口函數:卸載驅動時調用的函數
static void __exit hello_exit(void)
{
printk("[ default ] Hello Module Exit\n");
}
module_init(hello_init);
module_exit(hello_exit);
//MODULE_LICENSE("GPL2");
MODULE_AUTHOR("embedfire ");
MODULE_DESCRIPTION("hello world module");
MODULE_ALIAS("test_module");
2.2.2 內核模組命令
insmod:插入模組:insmod+模組完整路徑
。
modprobe:插入模組,insmod具備同樣的功能,同樣可以將模組載入到內核中,除此以外modprobe還能檢查模組之間的依賴關係, 並且按照順序載入這些依賴,可以理解為按照順序多次執行insmod。
depmod:創建模組依賴文件。modprobe是怎麼知道一個給定模組所依賴的其他的模組呢?在這個過程中,depmod起到了決定性作用,當執行modprobe時, 它會在模組的安裝目錄下搜索module.dep文件,這是depmod創建的模組依賴關係的文件。
rmmod:刪除模組:insmod+模組名稱
。
lsmod:查看所有模組。
modinfo:顯示模組中的幾個宏的定義。如協議、作者等等。
2.2.3 系統自動載入模組 **
讓系統自動載入模組需要用到命令depmod和modprobe。
首先需要將我們想要自動載入的模組統一放到 /lib/modules/內核版本 目錄下,內核版本使用 uname -r
查詢;
其次使用depmod建立模組之間的依賴關係,命令 depmod -a
;
這個時候我們就可以在modules.dep中看到模組依賴關係,可以使用如下命令查看:
cat /lib/modules/內核版本/modules.dep | grep calculation
最後在/etc/modules加上我們自己的模組。
- 注意在該配置文件中,模組不寫成.ko形式代表該模組與內核緊耦合,有些是系統必須要跟內核緊耦合,比如mm子系統, 一般寫成.ko形式比較好,如果出現錯誤不會導致內核出現panic錯誤,如果集成到內核,出錯了就會出現panic。
然後重啟開發板,使用命令lsmod即可查看我們的模組開機就被載入到內核了。
2.2.4 導出符號
實際上,符號指的就是內核模組中使用 EXPORT_SYMBOL 聲明的函數和變數。當模組被裝入內核後,它所導出的符號都會記錄在公共內核符號表中。可供給其它模組使用。
導出方法:
- 符號必須在模組文件的全局部分導出,不能在函數中使用。
- _GPL使得導出的模組只能被GPL許可的模組使用。
- 編譯我們的模組時,這兩個宏會被拓展為一個特殊變數的聲明,存放在ELF文件中。 具體也就是存放在ELF文件的符號表中:
- st_name: 是符號名稱在符號名稱字元串表中的索引值;
- st_value: 是符號所在的記憶體地址;
- st_size: 是符號大小;
- st_info: 是符號類型和綁定資訊;
- st_shndx: 表示符號所在section。
EXPORT_SYMBOL(name)
EXPORT_SYMBOL_GPL(name) // name為要導出的標誌
調用方法(例子):
extern int name;
2.2.5 模組參數
模組參數:模組參數是模組被載入時,可以傳值給模組中的參數。
Linux內核提供一個宏來實現模組的參數傳遞:
#define module_param(name, type, perm) module_param_named(name, name, type, perm)
#define module_param_array(name, type, nump, perm) module_param_array_named(name, name, type, nump, perm)
- name: 我們定義的變數名;
- type:參數的類型,目前內核支援的參數類型有byte,short,ushort,int,uint,long,ulong,charp,bool,invbool。其中charp表示的是字元指針,bool是布爾類型,其值只能為0或者是1;invbool是反布爾類型,其值也是只能取0或者是1,但是true值表示0,false表示1。變數是char類型時,傳參只能是byte,char * 時只能是charp。
- perm:表示的是該文件的許可權,具體參數值見下表:
對應用戶 | 字元 | 說明 |
---|---|---|
當前用戶 | S_IRUSR | 用戶具有讀許可權 |
當前用戶 | S_IWUSR | 用戶具有寫許可權 |
當前用戶組 | S_IRGRP | 當前用戶組的其它用戶擁有讀許可權 |
當前用戶組 | S_IWGRP | 當前用戶組的其它用戶擁有寫許可權 |
其它用戶 | S_IROTH | 其它用戶具有讀許可權 |
其它用戶 | S_IWOTH | 其它用戶具有寫許可權 |
模組參數使用示例
模組源碼:
static int nameA=0;
module_param(nameA,int,0);
static bool nameB=0;
module_param(nameB,bool,0644);
載入模組後,會在路徑 /sys/module/模組名/parameters 下存在以模組參數為名的文件。(註:若文件許可權為0,則無法查看該文件,也不會顯示在該路徑)
參考
- 李柱明部落格園
- 野火