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),加入新載入模塊提 供的所有資源和符號。所以另一個模塊被載入時, 它就可以調用所有已載入模塊提供的服務。
當卸掉一模塊時,內核先確定該模塊不會再被調用,然後通過某種方式通知它。在該模塊被內核卸掉以前,該模塊須釋放所有占用的系統資源。例如,內存或中斷,當模塊被卸掉後,內核從內核符號表中刪除所有該模塊提供的資源。
如果模塊代碼不嚴謹,它將使整個操作系統崩潰。另一個問題,如果你載入的是為其它版本服務的模塊,那怎麼辦?例如,一個模塊凋用一個內函數,但提供了錯誤的輸入參數,這將導致運行錯誤。但內核可以在模塊被載入時選擇性地通過嚴格版本檢查來杜絕這種現象。
載入模塊有兩種方法。第一種是通過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)。
顯示的是內核載入FAT 和VFAT 兩模塊後的模塊鏈表。鏈表的第一模塊並沒有顯示出來,那是一個偽模塊,只是用來記錄內核的輸出符號表。你可以用ISMOD命令來列出所有載入模塊及它們之間的關系。ISMOD只是格式化的輸出記錄內核鏈表的/PROC/MODULES文件。INSMOD 可以訪問內核分配給新載入模塊的內存,它先將模塊寫入這塊內存,然後對它進行重定位處理,使模塊可以從這個地址開始執行。由於每次模塊被載入時,無論在不在同一台機器上,都不大可能分配到相同的內存地址,所以重定位(即重設它的函數指針)是必須的。
新載入模塊也可以輸出符號,INSMOD 會為這些符號建一個表。另外,每一個模塊必須有自己的初始和清理(即析構)函數。這兩個函數不能被輸出,但它的地址將在初使化時由INSMOD 傳給內核。
當一個新模塊被載入內核時,它要更新系統符號表及被它調用的模塊。內核中被調用模塊都需在其符號表的最後保留一列指向調用模塊的指針。顯示VFAT文件系統依賴於FAT文件系統,所以,在FAT 模塊中有一個指向VFAT 的指針,這個指針是在VFAT被載入時加入的。內核將調用模塊的初使化函數,如果成功,它將繼續完成安裝新模塊的任務。模塊的清理函數的地址將被存在它的MODULE 數據結構中。當模塊被卸掉時,它將被調用。到這裡模塊的狀態被置為“運行“(RUNNING)。
卸掉模塊
用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,並從模塊鏈表中去除,所有它依賴的模塊會修改它們的指針,表示該模塊已不再依賴它們了。所有該模塊占用的內存將被釋放掉。
當一個模塊可以被卸掉時,系統會調用它的清理函數來釋放它所占用的所有系統資源。
該模塊的MODULE數據結構將被標為DELEDTED,並從模塊鏈表中去除,所有它依賴的模塊會修改它們的指針,表示該模塊已不再依賴它們了。所有該模塊占用的內存將被釋放掉。