410 likes | 492 Views
Chap 2_ 模組的製作與執行 Chap11_ 進階模組化與 kmod. 核心編譯與模組管理. 我們的系統僅認識二進制 ( binary ) 的可執行檔,因此,我們必須要將核心的原始碼編譯成為系統可以認識的執行檔才行 如果想要進行核心編譯,就必須要取得核心的原始碼才行 Linux 的核心相當的具有彈性,支援模組化的,也就是說,只要新硬體可以推出搭配核心的驅動模組,那麼我們只要將該模組掛載入核心,核心就可以支援該硬體. 開機的流程. 1 BIOS 2 MBR ( 就是 Lilo 或 Grub ) 取得 boot loader ;
E N D
核心編譯與模組管理 • 我們的系統僅認識二進制 ( binary ) 的可執行檔,因此,我們必須要將核心的原始碼編譯成為系統可以認識的執行檔才行 • 如果想要進行核心編譯,就必須要取得核心的原始碼才行 • Linux 的核心相當的具有彈性,支援模組化的,也就是說,只要新硬體可以推出搭配核心的驅動模組,那麼我們只要將該模組掛載入核心,核心就可以支援該硬體
開機的流程 1 BIOS 2 MBR ( 就是 Lilo 或 Grub ) 取得 boot loader; 3 取得核心 kernel 的資料並載入 4 init 5 scripts 6 login 與 shell • kernel 其實就是開機管理程式設定檔( lilo.conf 或 menu.lst )裡提到的那個檔案,通常也就是檔名為 vmlinuz 的這個檔案,放置在 /boot 裡頭,我們需要的編譯出來的核心檔案就是這一個東西 • 由於核心通常是支援模組的,那個外掛模組則是放置在 /lib/modules/`uname -r`裡面
核心的功能 • 用來控制系統硬體與相關程序的東西 • 核心可以使用『模組』的方式來動態的載入與移除所需要的模組 • 載入與卸載這些模組 • 核心的驅動程式寫在底下的目錄中: /lib/modules/核心版本/kernel/drivers /lib/modules/`uname –r`/kernel/drivers
depmod[root @test /root]# depmod [-aens] • 參數說明: -a :將 /etc/modules.conf 可以查詢到的模組的相關性都寫入 /lib/modules/`uname -r`/當中 -e :顯示出目前已載入的不可執行的模組名稱 -n :將各模組的相依性直接輸出螢幕上,而不是輸出到 /lib/modules/`uname -r` 當中 -s :將一些訊息結果輸出到 syslog 的登錄檔中! 範例: [root @test /root]# depmod -a [root @test /root]# depmod –n • 很多時候,在使用模組時,這些模組基本上是有相關性的,分析這些模組的相關性,則是以 depmod 來執行
lsmod[root @test /root]# lsmod • 範例: [root @test /root]# lsmod • 目前系統中的模組有哪些呢?就是以 lsmod 來查看
modinfo[root @test /root]# modinfo [-adnp] module_name • 參數說明: -a :秀出作者 -d :秀出這個模組的基本說明 -n :秀出這個模組的檔案放置路徑與名稱 範例: [root @test /root]# modinfo 8139too • 將這個模組的所有訊息都寫出來的意思
modprobe [root @test /root]# modprobe modules_name • 參數說明: -l :列出目前系統所有的模組 ( 在 /lib/modules/`uname -r`/kernel 底下 ) -c :列出目前系統所有的模組!(更詳細的代號對應表) 範例: [root @test /root]# modprobe ip_tables • 這個程式與底下的 insmod 有點類似,只是他是依據系統已經建立好的模組相依屬性來載入模組,而 insmod 則是直接手動來載入模組檔案
insmod[root @test /root]# insmod [-fkps] module_name • 參數說明: -f :強制將模組載入,要載入不相同版本的模組時候可能會用到的參數 -k :自動在核心沒有使用到該模組的時候,先清除乾淨! -p :測試模組是否可以被載入,不會直接載入系統中! -s :將一些執行訊息寫到 syslog 這種登錄檔,而不是寫到 terminal 當中! 範例: [root @test /root]# insmod -p /usr/local/src/dlink-530/via-rhine.o • 可以加上路徑與完整的檔名來載入模組,加上 -p 這個參數可以驗證該模組的可執行性與否
rmmod[root @test /root]# rmmod modules_name • 參數說明: 範例: [root @test /root]# rmmod 8139too • 輸入『 rmmod 模組名稱』就可以移除模組
#define MODULE #include <linux/kernel.h> #include <linux/module.h> int init_module(void) { printk(“<1>Hello,word\n”); return 0; } void cleanup_module(void) { printk(“<1>Goodby cruel world\n”); } □模組由insmod載入後模組就連接到核心因而能存取核心的公共符號 public symbol( 函式與變數 ). □printk()函式位於linux核心內部,類似標準c函式庫printf(). □核心需要自己的列印函式不能求助於c函式庫. □<1>代表訊息的優先等級 數值越低等級越高.
□指令 # insmod ./hello.o 顯示 Hello,world # rmmod hello 顯示 Goodbye cruel world □訊息會被紀錄在 /var/log /message 檔案. □教材:http://www.oreilly.com.tw/bookcode/ldd2-samples-1.0.2.tar.gz
核心模組 vs 應用程式 □應用程式啟動後從頭到尾都只執行同一件任務. □模組被載入核心之後必須先向核心註冊它自己. •init_module()函式(模組的入口點)任務是將模組的功能準備好 以便事後可被invocation(調用). •cleanup_module()在模組離開之前必須要被呼叫. □模組只能與核心連結所以模組只能呼叫核心所提供的程式 ex: printk(). □由於上一點,模組的原始碼不能引入一般的標頭檔. •有關核心相關事物放在 /usr/src/linux下的 include/linux 與 include/asm/ 目錄下的標頭檔.
□核心檔裡的許多宣告只跟核心本身有關且不應該被user-space的應用程式看到.□核心檔裡的許多宣告只跟核心本身有關且不應該被user-space的應用程式看到. □大部分宣告都放在 #ifdef _KERNEL_ 區間加以保護,故在編譯任何驅動模組或核心程式碼 必須先定義_KERNEL_巨集符號. □namespace pollution(命名空間污染) •函式或變數名稱不足以輕易辨別其意義或用途,或者為新符號命名苦惱所產生的困境. •解決:將自定符號定義為static並為全域性符號定一個專用的字首(prefixs)(小寫).
使用者空間與核心空間 □模組存活在kernel space(核心空間)而應用程式存在user space(使用者空間). □作業系統必須負責讓程式得以獨立運作並保護系統資源避免非授權的存取. □由CPU來保護系統軟體所以CPU本身提供了不同層級的作業模式(operating modality). □Unix系統提供兩個層級而現在CPU也至少有兩種層級,故Unix系統只使用最高與最低層級,Unix核心運作在最高層級(supervisor mode)應用程式運作在最低層級(user mode). □execution mode:包括kernel-space 與user-space分別有各自的memory mapping(記憶體對應關係)的關係與各自的address space(定址空間).
□應用程式發出system call 或 硬體中斷發生 user space切換到kernel space 執行system call的程式碼 或 中斷程序碼 □用來負責處理中斷程序的核心程式碼稱為interrupt handler. □所以模組主要在擴充核心的功能性,因此模組程式碼必須在kernel space內運行.
核心內的同步協調 □裝置驅動程式與應用程式之間最大的差別在於concurrency(共時性)的問題,應用程式採循序執行,核心程式需考慮到“同時”發生許多事的問題. □造成同時的原因 •驅動程式有可能同時被多個process所存取 •許多硬體裝置都有中斷(interrupt)處理器的能力 •有些軟體機制是以非同步方式運作(ex:kernel timer , chap6) •在Symmetric Multiprocessor(SMP,對稱式多處理器)系統上運作 □結論: •確保資料的一致性 •避免race condition的狀況,形成deadlack p1 p2
模組的編譯與裝載 □Makefile:可以幫我們編譯能被核心載入的目的碼(loadable object code). □Makefile •首先定義 __KERNEL__符號,才能使用核心標頭檔所定義大部分的核心內容. •在引入<linux/modules.h>之前必須先定義MODULES. •必須讓編譯器知道可最佳化到什麼程度. •加上-Wall(warning all).
□載入 •insmod對模組的作用:將模組內的任何unresolved symbol(懸置符號)連結到目前核心(函式庫)的符號表. •核心如何支援insmod? 依賴定義在kernel/module.c的system call函式: 1) sys_create_module():配置一塊可以容納模組的核心記憶空間. 2) sys_get_kernel_syms():傳回核心符號表,解決模組的懸置符號. 3) sys_init_module():將insmod改好的relocated object code移到 預先配置的核心空間.
版本依存性 □模組與連結對象核心息息相關每當升級和新版本時模組就必須在新版本核心下重新編譯一次. □編譯器會在ELF(executable Linking and Format).的.modifo(chap 11)區定義_module_kernel_version符號 □insmod會依照此符號與當時的核心作比較. □定義在<linux/module.h>. □可使用insmod –f來略過版本檢查. □針對特定版本的核心來編譯模組必須引入該版本核心的標頭檔在Makefile定義
□分層式模組化:為了不要讓一個模組獨立負擔太多功能,最好把通用的功能集中在一起,將需要視狀況而定的功能分別寫在不同的模組裡.□分層式模組化:為了不要讓一個模組獨立負擔太多功能,最好把通用的功能集中在一起,將需要視狀況而定的功能分別寫在不同的模組裡. □若不想釋出任何符號,在程式碼可加入巨集 EXPORT_NO_SYMBOLS; □若只想釋出部份符號可在引入<linux/module.h>之前定義EXPORT_SYMBOL巨集 •EXPORT_SYMBOL(name); •EXPORT_SYMBOL_NOVERS(name);
□載入的模組可在/proc/modules找到其資訊:名稱,佔用核心空間大小,用量計次,…□載入的模組可在/proc/modules找到其資訊:名稱,佔用核心空間大小,用量計次,… □
卸載模組 □使用rmmod可卸載(unload)沒用的模組. □原理: rmmod觸發delete_module() system call,如果模組的用量為零,則delete_module()會呼叫模組本身的cleanup_module();否則回傳作錯誤代碼. □cleanup_module()必須負責註銷該模組的每一項facility.
實際的初始化與清理函式 □ 命名模組函式,須引入<linux/init.h> •module_init(my_init); •module_exit(my_cleanup); □ my_init取代init_module(),my_cleanup取代cleanup_module(); □所以可使每個初始函式與清理函式都有自己專屬的名稱,以方便除錯.
資源的運用 □大部分驅動程式的工作大概就在讀寫I/O port或I/O memory(統稱 I/O region) □核心都應該保證驅動程式能獨占存取其I/O port以免受到其他驅動程式的干擾 □/proc/ioports與/proc/iomem檔案可取得以註冊的系統資源
□int check_region(unsigned long start,unsigned long len); •用來檢查某段範圍的I/O位址是否被佔用. □struct resource *request_region(unsigned long start,unsigned long len , char *nam); •要求註冊該位址區.若核心同意,此函式會回傳一個non-NULL指標.
□I/O port: 可從/proc/iomem檔案取得I/O memory的資訊
□取得,釋放特定一段的I/O memory region •int check_mem_region(usigned long start,unsigned long len); •int request_mem_region(unsigned long start,unsigned long len,char *name); •int release_mem_region(unsigned long start,unsigned long len);
決定組態參數 □驅動程式所需的某些參數會因系統而異,ex:I/O 位址,記憶區範圍…有時候必須傳遞參數給驅動程式,才能幫助他找到目標裝置或啟動,關閉特定功能. □對於某些裝置,如裝置品牌,型號…等也有可能影響驅動程式的行為. □將正確的參數傳遞給驅動程式(configuring),是驅動程式在初始化期間必須完成的工作.
□<linux/module.h>中其他三個能將文字放進目的檔的巨集.□<linux/module.h>中其他三個能將文字放進目的檔的巨集. •MODULE_AUTHOR(name): 放入作者名字. •MODULE_DESCRIPTION(desc):模組的敘述文字. •MODULE_SUPPORTED_DEVICE(dev):將模組的所支援的裝置放入目的檔.
User-space驅動程式 □優點 •可使用完整的c函式庫. •可使用傳統的debugger. •若user-space driver當掉,直接kill就可以. •user-space記憶體可被“置換(swappable)”,而kernel-space不可能,因此較大的程式不會排擠其他程式所使用的ram,除非此裝置正在使用. •user-space driver也能容許目標裝置同時被多個行程所存取.
□缺點: •在user-space內無法使用”中斷”. •不能直接存取I/O memory. •必須先呼叫ioperm()或iopl()才能存取I/O port. •回應時間較緩慢,因為應用程式要傳輸資料給硬體而硬體也要回應應用程式(context switch) •若驅動程式已被“置換(swap)”到磁碟上,那回應時間就更長了 •大部分重要裝置不能在user-space上予以有效控制,ex:網路介面…
□教材:http://www.oreilly.com.tw/bookcode/ldd2-samples-1.0.2.tar.gz□教材:http://www.oreilly.com.tw/bookcode/ldd2-samples-1.0.2.tar.gz □ http://linuxassembly.org/resources.html
自動載入模組 • 為了讓使用者更輕鬆載入 • 避免不常用的驅動程式長期佔據核心 • 為了做出可支援廣泛硬體的通用核心
在核心裡申請模組 --kmod的任務是負責載入模組的要求 • 在user-space的工作 --提供足夠的彈性來執行組態設定工作
模組的裝載與安全性 • 任何有能力將模組在入核心的人,都有機會控制整個系統 • Insmod通常會拒絕載入擁有者不是root的模組
模組內的版本控制 • 版本相依性問題 • 進行版本控制 : 1 makefile 2 模組程式本身 • 釋出版本化的符號 genksyms (.ver) 此程式從自己的標準輸入接收C preprocessor的輸出,然後印出新的標檔頭到標準輸出
更正書中錯誤 • <p.33>第14行程式碼: 原為:if(!item2 || !item2) 改為:if(!item1 || !item2) • <p.38>第15行: 原為:其/proc/iports 檔案的內容看起來像 改為:其/proc/ioports 檔案的內容看起來像
講的不好 多多包含 • 謝謝大家