Modules
本章描述 Linux 核心如何只在需要的時候才動態加載函數
例如文件系統
Linux 是一個完整的核心
就是說
它是一個單一的巨大的程序
核心的功能組件可以訪問它的所有的內部數據結構以及例程
另一種方法是使用一個微內核的結構
核心的功能片被分成獨立的單元
互相之間有嚴格的通訊機制
這樣通過配置進程向核心增加新的組件不花多少時間
比如你希望增加一個 NCR
SCSI 卡的 SCSI 驅動程序
你不需要把它連接到核心
否則你不得不配置並建立一個新的核心才能使用這個 NCR
作為一種變通
Linux 允許在你需要的時候動態地加載和卸載操作系統的組件
Linux 的模塊是可以在系統啟動之後任何時候動態連接到核心的代碼塊
它們可以在不被需要的時候從核心刪除並卸載
大多數 Linux 核心模塊是設備驅動程序
偽設備驅動程序比如網絡驅動程序或文件系統
你可以使用 insmod 和 rmmod 命令明確地加載和卸載 Linux 核心模塊
或者在需要這些模塊的時候由核心自己要求核心守護進程( kerneld )加載和卸載這些模塊
在需要的時候動態地加載代碼相當有吸引力
因為它讓核心可以保持最小而且核心非常靈活
我當前的 Intel 核心大量使用模塊
它只有
K 大小
我通常只適用 VFAT 文件系統
所以我建立我的 Linux 核心
當我安裝一個 VFAT 分區的時候自動加載 VFAT 文件系統
當我卸載 VFAT 文件系統的時候
系統探測到我不再需要 VFAT 文件系統模塊
把它從系統中刪除
模塊也可以用來嘗試新的核心代碼而不需要每次都創建和重啟動核心
但是
沒有這麼好的事情
使用核心模塊通常伴隨輕微的性能和內存開支
一個可加載模塊必須提供更多的代碼
這種代碼和額外的數據結構會占用更多一點的內存
另外因為間接訪問核心資源也讓模塊的效率輕微降低
一旦 Linux 核心加載
它就和普通核心代碼一樣成為核心的一部分
它和任何核心代碼擁有相同的權利和義務
換句話說
Linux 核心模塊和所有的核心代碼或設備驅動程序一樣可能讓核心崩潰
既然模塊在需要的時候可以使用核心資源
它們必須能夠找到這些資源
比如一個模塊需要調用 kmalloc()
核心內存分配例程
當建立的時候( build )
模塊不知道內存中 kmalloc() 在哪裡
所以當這個模塊加載的時候
在模塊能夠工作之前
核心必須整理模塊對於 kmmalloc() 的所有的引用
核心在核心符號表中保存了所有核心資源的列表
所以當模塊加載的時候它可以解析模塊中對於這些資源的引用
Linux 允許模塊堆棧(堆砌)
就是一個模塊需要另一個模塊的服務
例如 VFAT 文件系統模塊需要 FAT 文件系統模塊的服務
因為 VFAT 文件系統或多或少是 FAT 文件系統上的擴展
一個模塊需要另一個模塊的服務或資源的情況和一個模塊需要核心自己的服務和資源的情況非常相似
只不過這時請求的服務在另一個
此前已經加載的模塊鐘
當每一個模塊加載的時候
核心修改它的符號表
把這個新加載的模塊的所有輸出的資源或符號加到核心符號表中
這意味著
當下一個模塊加載的時候
它可以訪問已經加載的模塊的服務
當時圖卸載一個模塊的時候
核心需要知道這個模塊不在用
它還需要一些方法來通知它准備卸載的模塊
用這種方法模塊可以在它從核心刪除之前釋放它占用的任何的系統資源
例如核心內存或中斷
當模塊卸載的時候
核心把這個模塊輸出到核心符號表中所有的符號都刪除
除了寫的不好的可加載模塊可能破壞操作系統之外
還有另一個危險
如果你加載一個為比你當前運行的核心要早或遲的核心建立的模塊會發生什麼?如果這個模塊執行一個核心例程而提供了錯誤的參數就會引起問題
核心可以選擇防止這種情況
當模塊加載的時候進行嚴格的版本檢查
Loading a Module (加載一個模塊)
用兩種方法可以加載一個核心模塊
第一種使用 insmod 命令手工把它插入到核心
第二種
更聰明的方法是在需要的時候加載這個模塊
這叫做按需加載( demand loading )
當核心發現需要一個模塊的時候
例如當用戶安裝一個不在核心的文件系統的時候
核心會請求核心守護進程( kerneld )試圖加載合適的模塊
Kerneld 和 insmod
lsmod 以及 rmmod 都在 modules 程序包中
核心守護進程通常是擁有超級用戶特權的一個普通的用戶進程
當它啟動的時候(通常是在系統啟動的時候啟動)
它打開一個通向核心的 IPC 通道
核心使用這個連接向 kerneld 發送消息
請求它執行大量的任務
Kerneld 的主要功能是加載和卸載核心模塊
但是它也可以執行其它任務
比如需要的時候在串行線上啟動 PPP 連接
不需要的時候把它關閉
Kerneld 本身並不執行這些任務
它運行必要的程序比如 insmod 來完成工作
Kerneld 只是核心的一個代理
調度它的工作
參見 include/linux/kerneld
h
insmod 命令必須找到它要加載的被請求的核心模塊
按徐加載的核心模塊通常放在 /lib/mmodules/kernel
version 目錄裡邊
核心模塊和系統中的其它程序一樣是連接程序的目標文件
但是它們被連接成可以重定位的映像
就是沒有連接到特定地址去運行的映像
它們可以是 a
out 或 elf 格式的目標文件
Insmod 指向一個特權的系統調用
找出系統的輸出符號
它們以符號名稱和值(例如它的地址)的形式成對存放
核心的輸出符號表放在核心維護的模塊列表中的第一個 module 數據結構
用 module_list 指針指向
只有在核心編譯和連接的時候特殊指定的符號才加到這個表中
而並非核心的每一個符號都輸出它的模塊
例如符號
request_irq
是一個系統例程
當一個驅動程序希望控制一個特定的系統中斷的時候必須調用它
在我當前的核心上
它的值是
x
cd
你可以檢查文件 /proc/ksyms 或使用 ksyms 工具簡單地查看輸出的核心符號和它們的值
Ksyms 工具可以向你顯示所有的輸出的核心符號或者只顯示哪些加載模塊輸出的符號
Insmod 把模塊讀取到它的虛擬內存
使用核心的輸出符號來整理這個模塊對於核心例程和資源的未解析的引用
這個整理過程是用向內存中的模塊映像打補丁的方式進行
insmod 物理上把符號的地址寫到模塊的合適的位置
參見 kernel/module
c kernel_syms() include/linux/module
h
當 insmod 整理完了模塊對於輸出的核心符號的引用之後
他向核心請求足夠的空間放置新的核心
又是通過特權的系統調用
核心分配一個新的 module 數據結構和足夠的核心內存來存放這個新的模塊
並把它放置到核心的模塊列表的最後
這個新的模塊被標記為 UNINITIALIZED
圖
顯示了核心模塊列表的後面兩個模塊
FAT 和 VFAT 被加載到了內存
圖中沒有顯示的有列表的第一個模塊
這是一個偽模塊
用於放置核心的輸出符號表
你可以使用命令 lsmod 列出所有加載的核心模塊和它們之間的依賴關系
Lsmod 只是簡單地把從核心 module 數據結構列表中提取的 /proc/modules 重新安排了格式
核心為模塊分配的內存映射到 insmod 進程的地址空間
所以它可以訪問它
Insmod 把模塊拷貝到分配的空間
並把它重定位
這樣它就可以從被分配的核心地址運行
必須進行重定位
因為一個模塊不能期待在兩次被加載到相同的地址或者在兩個不同的 Linux 系統上被加載到相同的地址
這一次
重定位又關系到要用適當的地址為模塊的映像打補丁
參見 kernel/module
c create_module()
新的模塊也向核心輸出符號
Insmod 建立一個輸出映像表
每一個核心模塊必須包含模塊初始化和模塊清除的歷程
這些符號必須是專用的而不是輸出的
但是 insmod 必須知道它們的地址
能把它們傳遞給核心
所有這些做好之後
Insmod 現在准備初始化這個模塊
它執行一個特權的系統調用
把這個模塊的初始化和清除例程的地址傳遞給核心
參見 kernel/module
c sys_init_module()
當一個新的模塊加到核心的時候
它必須更新核心的符號表並改變被新的模塊使用的模塊
其它模塊依賴的模塊必須在它們的符號表之後維護一個引用列表
用它們的 module 數據結構指向
圖
顯示了 VFAT 文件系統模塊依賴於 FAT 文件系統模塊
所以 FAT 模塊包含一個到 VFAT 模塊的引用
這個引用在 VFAT 模塊加載的時候增加
核心調用模塊的初始化例程
如果成功
它開始安裝這個模塊
模塊的清除例程的地址保存在它的 module 數據結構中
當這個模塊卸載的時候核心會去調用
最後
模塊的狀態被設置為 RUNNING
Unloading a Module
模塊可以使用 rmmod 命令刪除
但是 kerneld 可以把所有不用的按需加載的模塊從系統中刪除
每一次它的空閒計時器到期的時候
kerneld 執行系統調用
請求從系統刪除所有的不需要的按需加載的模塊
這個計時器的值由你在啟動 kerneld 的時候設定
我的 kerneld 每
秒檢查一次
如果你安裝了一個 iso
CD ROM 而你的 iso
文件系統是一個可加載模塊
那麼
在 CD ROM 卸載不久
iso
模塊會從核心中刪除
如果核心中的其它組件依賴於一個模塊
它就不能被刪除
例如如果你安裝了一個或更多的 VFAT 文件系統
你就不能卸載 VFAT 模塊
如果你檢查 ls 輸出
你會看到每一個模塊關聯一個計數器
例如
Module: #pages: Used by:
msdos
vfat
(autoclean)
fat
[vfat msdos]
(autoclean)
這個計數器( count )是依賴於這個模塊的核心實體的數目
在上例中
vfat 和 msdos 都依賴於 fat 模塊
所以 fat 模塊的計數器是
Vfat 和 msdos 模塊的依賴數
From:http://tw.wingwit.com/Article/program/Oracle/201311/18169.html