Linux Kernel 核心中文手冊(12)--模塊

Modules
本章描述 Linux 核心如何只在需要的時候才動態加載函數,例如文件系統。
Linux 是一個完整的核心,就是說,它是一個單一的巨大的程序,核心的功能組件可以
訪問它的所有的內部數據結構以及例程。另一種方法是使用一個微內核的結構,核心的
功能片被分成獨立的單元,互相之間有嚴格的通訊機制。這樣通過配置進程向核心增加
新的組件不花多少時間。比如你希望增加一個 NCR 810 SCSI 卡的 SCSI 驅動程序,你
不需要把它連接到核心。否則你不得不配置並建立一個新的核心才能使用這個 NCR 810
 。作爲一種變通, Linux 允許在你需要的時候動態地加載和卸載操作系統的組件。 L
inux 的模塊是可以在系統啓動之後任何時候動態連接到核心的代碼塊。它們可以在不被
需要的時候從核心刪除並卸載。大多數 Linux 核心模塊是設備驅動程序,僞設備驅動程
序比如網絡驅動程序或文件系統。
你可以使用 insmod 和 rmmod 命令明確地加載和卸載 Linux 核心模塊,或者在需要這
些模塊的時候由核心自己要求核心守護進程( kerneld )加載和卸載這些模塊。在需要


的時候動態地加載代碼相當有吸引力,因爲它讓核心可以保持最小而且核心非常靈活。
我當前的 Intel 核心大量使用模塊,它只有 406K 大小。我通常只適用 VFAT 文件系統
,所以我建立我的 Linux 核心,當我安裝一個 VFAT 分區的時候自動加載 VFAT 文件系
統。當我卸載 VFAT 文件系統的時候,系統探測到我不再需要 VFAT 文件系統模塊,把
它從系統中刪除。模塊也可以用來嘗試新的核心代碼而不需要每次都創建和重啓動核心
。但是,沒有這麼好的事情,使用核心模塊通常伴隨輕微的性能和內存開支。一個可加
載模塊必須提供更多的代碼,這種代碼和額外的數據結構會佔用更多一點的內存。另外
因爲間接訪問核心資源也讓模塊的效率輕微降低。
一旦 Linux 核心加載,它就和普通核心代碼一樣成爲核心的一部分。它和任何核心代碼
擁有相同的權利和義務:換句話說, Linux 核心模塊和所有的核心代碼或設備驅動程序
一樣可能讓核心崩潰。
既然模塊在需要的時候可以使用核心資源,它們必須能夠找到這些資源。比如一個模塊
需要調用 kmalloc() ,核心內存分配例程。當建立的時候( build ),模塊不知道內
存中 kmalloc() 在哪裏,所以當這個模塊加載的時候,在模塊能夠工作之前,核心必須
整理模塊對於 kmmalloc() 的所有的引用。核心在覈心符號表中保存了所有核心資源的
列表,所以當模塊加載的時候它可以解析模塊中對於這些資源的引用。 Linux 允許模塊
堆棧(堆砌),就是一個模塊需要另一個模塊的服務。例如 VFAT 文件系統模塊需要 F
AT 文件系統模塊的服務,因爲 VFAT 文件系統或多或少是 FAT 文件系統上的擴展。一
個模塊需要另一個模塊的服務或資源的情況和一個模塊需要核心自己的服務和資源的情
況非常相似,只不過這時請求的服務在另一個,此前已經加載的模塊鍾。當每一個模塊
加載的時候,核心修改它的符號表,把這個新加載的模塊的所有輸出的資源或符號加到
核心符號表中。這意味着,當下一個模塊加載的時候,它可以訪問已經加載的模塊的服


務。
當時圖卸載一個模塊的時候,核心需要知道這個模塊不在用,它還需要一些方法來通知
它準備卸載的模塊。用這種方法模塊可以在它從核心刪除之前釋放它佔用的任何的系統
資源,例如核心內存或中斷。當模塊卸載的時候,核心把這個模塊輸出到核心符號表中
所有的符號都刪除。
除了寫的不好的可加載模塊可能破壞操作系統之外,還有另一個危險。如果你加載一個
爲比你當前運行的核心要早或遲的核心建立的模塊會發生什麼?如果這個模塊執行一個
核心例程而提供了錯誤的參數就會引起問題。核心可以選擇防止這種情況,當模塊加載
的時候進行嚴格的版本檢查。
12.1 Loading a Module (加載一個模塊)
用兩種方法可以加載一個核心模塊。第一種使用 insmod 命令手工把它插入到核心。第
二種,更聰明的方法是在需要的時候加載這個模塊:這叫做按需加載( demand loadin
g )。當核心發現需要一個模塊的時候,例如當用戶安裝一個不在覈心的文件系統的時
候,核心會請求核心守護進程( kerneld )試圖加載合適的模塊。
Kerneld 和 insmod , lsmod 以及 rmmod 都在 modules 程序包中。
核心守護進程通常是擁有超級用戶特權的一個普通的用戶進程。當它啓動的時候(通常
是在系統啓動的時候啓動),它打開一個通向核心的 IPC 通道。核心使用這個連接向
kerneld 發送消息,請求它執行大量的任務。 Kerneld 的主要功能是加載和卸載核心模
塊,但是它也可以執行其它任務,比如需要的時候在串行線上啓動 PPP 連接,不需要的
時候把它關閉。 Kerneld 本身並不執行這些任務,它運行必要的程序比如 insmod 來完
成工作。 Kerneld 只是核心的一個代理,調度它的工作。
參見 include/linux/kerneld.h


insmod 命令必須找到它要加載的被請求的核心模塊。按徐加載的核心模塊通常放在 /l
ib/mmodules/kernel-version 目錄裏邊。核心模塊和系統中的其它程序一樣是連接程序
的目標文件,但是它們被連接成可以重定位的映像。就是沒有連接到特定地址去運行的
映像。它們可以是 a.out 或 elf 格式的目標文件。 Insmod 指向一個特權的系統調用
,找出系統的輸出符號。它們以符號名稱和值(例如它的地址)的形式成對存放。核心
的輸出符號表放在覈心維護的模塊列表中的第一個 module 數據結構,用 module_list
 指針指向。只有在核心編譯和連接的時候特殊指定的符號才加到這個表中,而並非核心
的每一個符號都輸出它的模塊。例如符號“ request_irq ”是一個系統例程,當一個驅
動程序希望控制一個特定的系統中斷的時候必須調用它。在我當前的核心上,它的值是
 0x0010cd30 。你可以檢查文件 /proc/ksyms 或使用 ksyms 工具簡單地查看輸出的核
心符號和它們的值。 Ksyms 工具可以向你顯示所有的輸出的核心符號或者只顯示哪些加
載模塊輸出的符號。 Insmod 把模塊讀取到它的虛擬內存,使用核心的輸出符號來整理
這個模塊對於核心例程和資源的未解析的引用。這個整理過程是用向內存中的模塊映像
打補丁的方式進行, insmod 物理上把符號的地址寫到模塊的合適的位置。
參見 kernel/module.c kernel_syms() include/linux/module.h
 
當 insmod 整理完了模塊對於輸出的核心符號的引用之後,他向核心請求足夠的空間放
置新的核心,又是通過特權的系統調用。核心分配一個新的 module 數據結構和足夠的
核心內存來存放這個新的模塊,並把它放置到核心的模塊列表的最後。這個新的模塊被
標記爲 UNINITIALIZED 。圖 12.1 顯示了核心模塊列表的後面兩個模塊: FAT 和 VFA
T 被加載到了內存。圖中沒有顯示的有列表的第一個模塊:這是一個僞模塊,用於放置
核心的輸出符號表。你可以使用命令 lsmod 列出所有加載的核心模塊和它們之間的依賴


關係。 Lsmod 只是簡單地把從核心 module 數據結構列表中提取的 /proc/modules 重
新安排了格式。核心爲模塊分配的內存映射到 insmod 進程的地址空間,所以它可以訪
問它。 Insmod 把模塊拷貝到分配的空間,並把它重定位,這樣它就可以從被分配的核
心地址運行。必須進行重定位,因爲一個模塊不能期待在兩次被加載到相同的地址或者
在兩個不同的 Linux 系統上被加載到相同的地址。這一次,重定位又關係到要用適當的
地址爲模塊的映像打補丁。
參見 kernel/module.c create_module()
新的模塊也向核心輸出符號, Insmod 建立一個輸出映像表。每一個核心模塊必須包含
模塊初始化和模塊清除的歷程,這些符號必須是專用的而不是輸出的,但是 insmod 必
須知道它們的地址,能把它們傳遞給核心。所有這些做好之後, Insmod 現在準備初始
化這個模塊,它執行一個特權的系統調用,把這個模塊的初始化和清除例程的地址傳遞
給核心。
參見 kernel/module.c sys_init_module()
當一個新的模塊加到核心的時候,它必須更新核心的符號表並改變被新的模塊使用的模
塊。其它模塊依賴的模塊必須在它們的符號表之後維護一個引用列表,用它們的 modul
e 數據結構指向。圖 12.1 顯示了 VFAT 文件系統模塊依賴於 FAT 文件系統模塊。所以
 FAT 模塊包含一個到 VFAT 模塊的引用:這個引用在 VFAT 模塊加載的時候增加。核心
調用模塊的初始化例程,如果成功,它開始安裝這個模塊。模塊的清除例程的地址保存
在它的 module 數據結構中,當這個模塊卸載的時候核心會去調用。最後,模塊的狀態
被設置爲 RUNNING 。
12.2 Unloading a Module
模塊可以使用 rmmod 命令刪除,但是 kerneld 可以把所有不用的按需加載的模塊從系


統中刪除。每一次它的空閒計時器到期的時候, kerneld 執行系統調用,請求從系統刪
除所有的不需要的按需加載的模塊。這個計時器的值由你在啓動 kerneld 的時候設定:
我的 kerneld 每 180 秒檢查一次。如果你安裝了一個 iso9660 CD ROM 而你的 iso96
60 文件系統是一個可加載模塊,那麼,在 CD ROM 卸載不久, iso9660 模塊會從核心
中刪除。
如果核心中的其它組件依賴於一個模塊,它就不能被刪除。例如如果你安裝了一個或更
多的 VFAT 文件系統,你就不能卸載 VFAT 模塊。如果你檢查 ls 輸出,你會看到每一
個模塊關聯一個計數器。例如:
Module: #pages: Used by:
msdos 5 1
vfat 4 1 (autoclean)
fat 6 [vfat msdos] 2 (autoclean)
這個計數器( count )是依賴於這個模塊的核心實體的數目。在上例中, vfat 和 ms
dos 都依賴於 fat 模塊,所以 fat 模塊的計數器是 2 。 Vfat 和 msdos 模塊的依賴
數都是 1 ,因爲它們都有一個安裝的文件系統。如果我加載另外一個 VFAT 文件系統,
那麼 vfat 模塊的計數器會變成 2 。一個模塊的計數器放在它的映像的第一個長字中(
 longword )。
因爲它也放置 AUTOCLEAN 和 VISITED 標誌,所以這個字段有一些輕微過載。這些標誌
都用於按需加載模塊。這些模塊被標記爲 AUTOCLEAN ,這樣系統可以識別出哪些它可以
自動卸載。 VISITED 標誌表示這個模塊被一個或多個系統組件使用:只要另一個組件使
用它就設置這個標誌。每一次 kerneld 請求系統刪除不用的按需加載的模塊的時候,它
都查看系統中所有的模塊,找到合適的候選。它只查看標記爲 AUTOCLEAN 而且狀態是


它就清除這個 VISITED 標記,繼續查找系統中的下一個模塊。
假設一個模塊可以被卸載,就調用它的清除例程( cleanup ),讓它釋放它所分配的核
心資源。這個 module 數據結構被標記爲 DELTED ,從核心模塊列表中刪除。任何其它
的它所依賴的模塊的引用表被修改,這樣它們不再把它當作一個依賴者。這個模塊需要
的所有的核心內存被釋放。
參見 kernel/module.c delete_module()

發佈了0 篇原創文章 · 獲贊 0 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章