第 12 章 模組


本章將講述Linux核心是如何按需動態裝載和卸掉模組的。 Linux是單核心結構,也就是說, 它是一個大程式, 其中任一函數都可以存取公共 資料結構和其它函數調用。 (作為作業系統)另外一種可能的結構是多核式的, 各 功能塊自成一體, 相互之間由嚴格的通信機制相連。單核結構在添加新模組時,一 種方法是重新調整設置,所以非常費時。 比如,你想在核心中加一個NCR 810 SCSI 的驅動程式, 你必須重新設置, 重建核心。這也有另外一個辦法, Linux 允許動 態裝載和卸掉模組. Linux 模組是一段可以在機器起動後任意時間被動態連接的代 碼. 在不需要時, 它們可以被從核心中卸掉. 大多數Linux 模組是設備驅動程式或 偽設備驅動程式, 如網路驅動程式, 檔案系統等. 你可以使用 insmod 和 rmmod 命令來裝載和卸掉 Linux 模組, 核心自己也可以調 用核心駐留程式(Kerneld) 來按需要裝載和卸掉模組. 按需動態裝載模組可以使核心保持最小, 並更具靈活性. 我現在的 Intel 核心由於 大量使用動態裝載模組, 只有 406 K 位元. 例如, 我很少用到 VFAT 檔案系統, 所以我讓 Linux 核心只在我裝載 VFAT 分區時, 才自動上載 VFAT 檔案系統. 當我 卸掉 VFAT 分區時, 核心會檢測到, 並自動卸掉 VFAT 檔案系統. 當測試新程式時 , 你如果不想每次都重建核心, 動態裝載模組是非常有用的. 但是, 運用模組會多 消耗一些記憶體, 並對速度有一定影響. 並且模組裝載程式是一段程式碼, 它的資料將 占用一部份記憶體. 這樣還會造成不能直接存取核心資源, 效率不高的問題. 一旦 Linux 模組被裝載後, 它就和一般核心程式碼一樣, 對其它核心程式碼, 享受同樣 的存取權限。換句話說, Linux 核心模組可以像其它核心程式碼, 或驅動程式一樣使 系統崩潰。 模組可以使用核心資源,但首先它需知道怎樣調用. 例如, 一個模組要調用 Kmalloc() (核心記憶體分配程式). 但在模組建立時, 它並不知道到哪兒去找 Kmalloc(), 所以 在它被裝載時, 核心必須先設定模組中所有 Kmalloc() 調用的函數指標. 核心有一 張所有資源調用的列表, 在模組被裝載時, 核心重設所有資源調用的函數指標. Linux 允許堆疊式模組, 即一個模組調用另一個模組的函數. 例如, 由於 VFAT 檔案系統可 以看成是 FAT 檔案系統的超集, 所以 VFAT 檔案系統模組需要調用 FAT 檔案系統 提供的服務。一個模組調用另一模組的資源與調用核心資源很相似。唯一的不同是 被調用的模組需被先載入。一個模組被載入後,核心將修改它的核心符號表(KERAEL SYMOBOL TABLE),加入新載入模組提 供的所有資源和符號。所以另一個模組被載入 時, 它就可以調用所有已載入模組提供的服務。 當卸掉一模組時,核心先確定該模組不會再被調用,然後通過某種方式通知它。在 該模組被核心卸掉以前,該模組須釋放所有占用的系統資源。例如,記憶體或中斷, 當模組被卸掉後,核心從核心符號表中刪除所有該模組提供的資源。 如果模組程式碼不嚴謹,它將使整個作業系統崩潰。另一個問題,如果你載入的是為 其它版本服務的模組,那怎麼辦?例如,一個模組凋用一個內函數,但提供了錯誤 的輸入參數,這將導致運行錯誤。但核心可以在模組被載入時選擇性地通過嚴格版 本檢查來杜絕這種現像。 12。1 載入一個模組
圖 12.1 核心模組鏈結串列
載入模組有兩種方法。第一種是通過INSTALL 命令來載入﹔ 另一種更聰明的方法是在模組被調用時自動載入,這叫所需載入(DEMAND LOADING)。 例如,當用戶在裝一個不在核心中的檔案系統,核心會自動調用核心駐 留程式(KERNELD) 來載入對應的處理模組。 核心駐留程式是一個具有超級用戶極限的普通用戶程式。當它被啟動時(通常在系統 啟 動時),它將打開一個和核心之間的程序間通信管道(IPC CHANNEL)。 核心將利用這 條 管道來通知程序駐留程式去完成各種任務。 核心駐留程式的主要任務是載入和卸掉模組,它也能完成其她一些任務。如按需打 開和關掉一條通過串口的 DDD LINK。KERNELD 自己並不完成這些任務。它將調用如 INSMOD 這樣的命令來完成,KERNELD 只是一個核心的代理,協調完成各項任務。 載入模組時,INSMODE 命令必須先找到要被載入的模組。可所需載入的模組通常被 放在/LIB/MODULES/KERNEL-VERSION下,這些模組與一般系統程式都是已連接好的目 標程式碼,不同處在於模組是可重定位的映像檔案。也就是說,模組並不是從一個固 定的位址開始執行的。模組可以是 a.out, 也可以是ELF格式的目標程式碼。INSMODE 通過一個有系統權限的調用來找到核心中可被調用的資源。 系統(資源)符號由名和值倆部份組成。核心用MODULE_LIST 指標指向其管理的所有 模組所串成的鏈結串列。核心的輸出符號表在第一個MODULE 資料結構中,並不是核心所 有的符號都能被模組調用,可調用符號必須被加入輸出符號表中,而輸出符號表是 與核心一起編譯連接的。例如,當一驅動程式想控制某一系統中斷時,她需調用” REQUEST_IRQ”這樣一個系統函數,在我機器的核心中,它現在的值是0x0010cd30, 你可以看/PROC/KSYMS檔案或用KSYMS 來查詢。KSYMS 命令可以顯示所有核心輸出符 號的值,也可以顯示載入模組的輸出符號的值。當INSMOD 載入模組時,它先將模組 載入虛擬記憶體,根據核心輸出符號,重設所有核心資源函數調用的指標。即在模組的函 數調用處寫入對應符號的實體位址。 當INSMOD 重設完核心輸出符號的位址後,它將調用一個系統函數,要 求核心分配 足夠的空間。記憶體就會分配一個新的MODULE 資料結構和足夠的記憶體來裝載這個新模 塊,並把這個MODULE 資料結構放在模組鏈結串列的最後, 置成未初使化(UNINITALIZED)。 表12。1顯示的是核心載入FAT 和VFAT 兩模組後的模組鏈結串列。鏈結串列的第一模組並沒 有顯示出來,那是一個偽模組,只是用來記錄核心的輸出符號表。你可以用ISMOD命 令來列出所有載入模組及它們之間的關系。ISMOD只是格式化的輸出記錄核心鏈結串列的 /PROC/MODULES檔案。INSMOD 可以存取核心分配給新載入模組的記憶體,它先將模組 寫入這塊記憶體,然後對它進行重定位處理,使模組可以從這個位址開始執行。由於 每次模組被載入時,無論在不在同一台機器上,都不大可能分配到相同的記憶體位址, 所以重定位(即重設它的函數指標)是必須的。 新載入模組也可以輸出符號,INSMOD 會為這些符號建一個表。另外,每一個模組必 須有自己的初始和清理(即析構)函數。這兩個函數不能被輸出,但它的位址將在初 使化時由INSMOD 傳給核心。 當一個新模組被載入核心時,它要更新系統符號表及被它調用的模組。核心中被調 用模組都需在其符號表的最後保留一列指向調用模組的指標。圖12。1顯示VFAT檔案 系統依賴於FAT檔案系統,所以,在FAT 模組中有一個指向VFAT 的指標,這個指標 是在VFAT被載入時加入的。核心將調用模組的初使化函數,如果成功,它將繼續完 成安裝新模組的任務。模組的清理函數的位址將被存在它的MODULE 資料結構中。當 模組被卸掉時,它將被調用。到這裡模組的狀態被置為“運行“(RUNNING)。 12。2 卸掉模組 用RMMOD命令可以卸掉一個指定模組,但按需載入模組沒用時,它會被核心自動卸掉, KERNELD每次被啟動時,它會調用一個系統函數將所有沒用的模組從核心中卸掉。例 如,如果你裝了一個ISO9660的CDROM,並且它的檔案系統是一個按需載入模組,那 麼當你卸掉CDROM 後不久,ISO9660檔案系統也會被從核心中卸掉。你可以在起動KERNELD 時,設置其被啟動的時間間隔,我的KERNELD 每180秒被啟動一次。 當還在被其他模組調用時,模組是不能被卸掉的。例如,當你還在用VFAT檔案系統 時,VFAT模組不會被卸掉。當你看ISMOD 命令的輸出時,你會發現每個模組都帶有 一個計數器。這個計 數器記錄依賴於該模組的模組數。在上面的例子中, VFAT 和 MSDOS 都依賴於FAT模組,所以FAT 模組的計數器為2,VFAT 和MODOS的都為1,表示 只有檔案系統依賴於它們。如果我再裝入一個VFAT檔案系統,VFAT模組的計數器將 變成2。模組的計數器是它映像的第一個長字(LONGWORD)。 這個長字同時也記錄了AUTOCLEAN 和 VISITED 兩個標誌,只有按需載入模組才用到 這兩個標誌。AUTOCLEAN用來使系統識別哪一個模組需被自動卸掉。VISITED 標誌表 示該模組是否還在被其它模組調用。每次KERNELD 試圖卸掉已沒用的按需載入模組 時,系統檢查所有模組。它只注意標為AUTOCLEAN並正在運行的模組,如果這個模組 沒有設置VISTIED 標誌,它將被卸掉。否則,系統就清掉VISTIED標誌,並繼續檢查 下一模組。 當一個模組可以被卸掉時,系統會調用它的清理函數來釋放它所占用的所有系統資 源。 該模組的MODULE資料結構將被標為DELEDTED,並從模組鏈結串列中去除,所有它依賴的 模組會修改它們的指標,表示該模組已不再依賴它們了。所有該模組占用的記憶體將 被釋放掉。