1. 程式人生 > >Linux下驅動模塊學習

Linux下驅動模塊學習

14. 移除 return 加載 控制器 就是 偏移地址 模塊插入 出現

1.modutils中提供了相關的insmod,rmmod,modinfo工具
2.modprobe在識別出目標模塊所依賴模塊後也是調用insmod.
3.從外部看模塊只是普通可重定位的目標文件。可重定位文件的函數都不會引用絕對地址,而只是指向代碼中的相對地址,因此可以在內存
中的任意偏移地址加載。
4.加載模塊只需要一個系統調用init_module,即可在內核中完成所有的操作。
5.nm工具可用於產生模塊(或任意目標文件)中所有的外部函數列表。
# nm atmel_mxt_ts.ko
# nm a.out
U代表未解決的引用;T表示位於代碼段;D表示位於數據段

6.配置 CONFIG_KALLSYMS 選項為y即可在內核中啟用 kallsyms 功能:
有的符號是大寫的,有的是小寫。大寫的符號是全局的。
b 符號在BSS段中
c 普通符號,是未初始化區域
d 符號在數據段中
g 符號針對小object,在初始化數據區
i 非直接引用其他符號的符號
n 調試符號
r 符號在只讀存儲區
s 符號針對小object,在未初始化數據區
t 符號在代碼段
u 符號引用還未解決

7.modutils標準工具中的depmod工具可用於計算系統各個模塊之間的依賴關系,每次啟動或安裝模塊時就會運行該程序,默認會將
依賴關系保存到/lib/modules/version/module.dep中,表示格式B:A表示模塊B依賴於模塊A

8./proc/kallsyms中列出了內核導出的所有符號(早些版本應該是/lib/modules/version/System.map),模塊中未解決引用的符號會在這裏面找。

9.內核中模塊的信息存放在.modinfo段中。模塊中的信息可以使用modinfo來看。

10.模塊的自動加載與熱插拔
1)在用戶空間完成模塊加載比在內核空間完成模塊加載更方便,內核將該工作委托給kmod進程。註意kmod並不是一個永久守護進程,內核會
按需啟用它。

2)eg:mount -t vfat /dev/fd0 /mnt/floppy 若之前內核中沒有vfat.ko,這時會先插入這個驅動模塊,在mount返回後,所需的模塊已經載
入內核了。執行過程:內核發現其數據結構中沒有vfat的信息 --> 內核請求 --> modprobe --> 模塊查找 --> request_module --> 加載模塊
request_module
├── 為modeprob準備環境
├── 同時調用request_module的次數過多(50個)?==> return
└── call_usermodehelper
modprobe_path通常是/sbin/modprobe,但是可以通過/proc/sys/kernel/modprobe或sysctl改變。

3).有時候內核需要加載模塊的時候可能會出現無法唯一確定哪個模塊能提供所需的功能,因此產生模塊別名(module alias)
eg:U盤插入到系統中,被主機控制器識別為新設備,我們知道需要裝載的模塊時usb-storage,但是內核是如何知道的呢:
附加在每個模塊上都有一個“小小的數據庫”,裏面描述了該模塊所支持的設備。對於USB設備,數據庫的信息包括所支持的接口類型列表,
廠商ID或能夠標識設備的任意類型信息。
數據庫信息通過模塊別名(module alias)提供,它是模塊的通用標識。modules.h中的MODULE_INFO、MODULE_ALIAS宏,eg raid5.c
udevd實現模塊的熱插拔也是使用了這個信息。

4)比直接別名更重要的是設備數據庫,內核使用宏MODULE_DEVICE_TABLE來實現這樣的數據庫。

5)add_uevent_var() 位於kobject_uevent.c中,可以用於向uevent中的插拔消息提供一個新的鍵值對!
通過比較MODALIAS值和各個模塊提供的別名,udevd可以找到需要插入的模塊。
①模塊代碼中使用MODULE_ALIAS指定別名;
②設備插入時的udev事件中使用下面方法上報設備的別名
add_uevent_var(env, "MODALIAS=virtio:d%08Xv%08X", dev->id.device, dev->id.vendor);
這個函數這個使用方法在內核中還存在,應該沒有被遺棄!!

11.模塊的插入和刪除
1)兩個系統調用,init_module() 和 delete_module(), 可以使用man 2 來看
init_module
├── load_module
├── 將模塊插入到內核鏈表
├── mod->init
└── 釋放初始化數據/代碼占用的區域

sys_delete_module
├── find_module
├── 確認模塊未被使用
├── mod->exit
└── free_module


2)模塊狀態:
enum module_state {
MODULE_STATE_LIVE, /*模塊正常運行時*/
MODULE_STATE_COMING, /*模塊裝載期間*/
MODULE_STATE_GOING, /*模塊正在移除*/
MODULE_STATE_UNFORMED, /* Still setting it up. */
};

12.license_is_gpl_compatible()用來判斷給定許可證是否GPL兼容
13.在模塊自身和所依賴的所有其它模塊都已經編譯完成之前,模塊中的有些段是無法生成的
14.模塊的初始化和清理函數保存在.gnu.linkonce.module段中的module實例中,該實例位於每個模塊自動生成的附加文件中。名為module.mod.c中
15.使用EXPORT_SYMBOL()導出符號,它會在__ksymtab段中產生一個結構。此宏包含的__CRC_SYMBOL()來實現版本控制,主語模塊的版本控制是per-symbol
的。
16. 模塊信息常用宏
MODULE_INFO 一般模塊信息
MODULE_LICENSE 模塊許可證
MODULE_AUTHOR 模塊作者
MODULE_DESCRIPTION 模塊描述
MODULE_ALIAS 指定模塊別名(udevd使用它執行熱插拔加載模塊)

17.版本控制
1)基本版本控制
module.mod.c中MODULE_INFO(vermagic, VERMAGIC_STRING)
#define VERMAGIC_STRING \
UTS_RELEASE " " \ /*字符串形式的內核版本*/
MODULE_VERMAGIC_SMP MODULE_VERMAGIC_PREEMPT \
MODULE_VERMAGIC_MODULE_UNLOAD MODULE_VERMAGIC_MODVERSIONS \
MODULE_ARCH_VERMAGIC \
MODULE_RANDSTRUCT_PLUGIN
展開就是:"4.14.0 SMP preempt mod_unload aarch64"

內核和模塊中都會存儲VERMAGIC_STRING的一份副本。只有這兩份副本匹配時,模塊才能加載。這意味著模塊和內核的下列方面必須一致才能加載:
①SMP配置(是否啟用)
②搶占配置(是否啟用)
③使用的編譯器版本
④特定於體系結構的常數

註意:基本版本控制中內核的版本雖然會存儲,但是在進行比較時會忽略。因此內核版本不同的模塊,只要剩余版本字符串匹配,這個檢查就不影
響模塊的裝載。

2)per-symbol的版本控制
在EXPORT_SYMBOL()中有個__CRC_SYMBOL()來產生此symbol的crc,加載的時候會比較
版本控制函數check_version()

18.內核不僅在設備插入與移除時向用戶空間提供消息,實際上內核在很多一般事件發生時都會發送消息。模塊的插入與移除也會.
設備模型的每一個部分都可以向用戶層發送註冊和撤銷註冊事件。

19.將module.info.c文件編譯成目標文件,並使用ld將其與模塊現存的.o目標文件鏈接起來,結構命名為module.ko,這就是最終的模塊!

20.不僅設備驅動程序可以編譯成模塊,內核中除了最基礎部分外都可以編譯成模塊。

Linux下驅動模塊學習