模組程式設計屬於核心程式設計,因此,除了對核心相關知識有所了解外,還需要了解與模組相關的知識。
1.應用程式與核心模組的比較
為了加深對核心模組的了解,表一給出應用程式與核心模組程式的比較。
表一 應用程式與核心模組程式的比較
c語言應用程式
核心模組程式
使用函式
libc庫
核心函式
執行空間
使用者空間
核心空間
執行許可權
普通使用者
超級使用者
入口函式
main()
module_init()
出口函式
exit()
module_exit()
編譯gcc –c
makefile
連線gcc
insmod
執行直接執行
insmod
除錯gdb
kdbug, kdb,kgdb等
從表一我們可以看出,核心模組程式不能呼叫libc庫中的函式,它執行在核心空間,且只有超級使用者可以對其執行。另外,模組程式必須通過module_init()和module-exit()函式來告訴核心「我來了」和「我走了」。
2.核心符號表(如果對以下第2~4點理解上有困難,可以越過)
如 前所述,linux核心是乙個整體結構,像乙個圓球,而模組是插入到核心中的外掛程式。儘管核心不是乙個可安裝模組,但為了方便起見,linux把核心也看作 乙個「母」模組。那麼模組與模組之間如何進行互動呢,一種常用的方法就是共享變數和函式。但並不是模組中的每個變數和函式都能被共享,核心只把各個模組中 主要的變數和函式放在乙個特定的區段,這些變數和函式就統稱為符號。到低哪些符號可以被共享? linux核心有自己的規定。對於核心這個特殊的母模組,在kernel/ksyms.c中定義了從中可以「移出」的符號,例如程序管理子系統可以「移出」的符號定義如下:
/* 程序管理 */
export_symbol(do_mmap_pgoff);
export_symbol(do_munmap);
export_symbol(do_brk);
export_symbol(exit_mm); …
export_symbol(schedule);
export_symbol(jiffies);
export_symbol(xtime); …
你可能對這些變數和函式已經很熟悉。其中巨集定義export_symbol()本身的含義是「移出符號」。為什麼說是「移出」呢?因為這些符號本來是核心內部的符號,通過這個巨集放在乙個公開的地方,使得裝入到核心中的其他模組可以引用它們。
實際上,僅僅知道這些符號的名字是不夠的,還得知道它們在核心位址空間中的位址才有意義。因此,核心中定義了如下結構來描述模組的符號:
struct module_symbol ;
我們可以從/proc/ksyms檔案中讀取所有核心模組「移出」的符號,這所有符號就形成核心符號表,其格式如下:
記憶體位址 符號名 [所屬模組]
在模組程式設計中,可以根據符號名從這個檔案中檢索出其對應的位址,然後直接訪問該位址從而獲得核心資料。第三列「所屬模組」指符號所在的模組名,對於從核心這一母模組移出的符號,這一列為空。
模組載入後,2.4核心下可通過 /proc/ksyms、 2.6 核心下可通過/proc/kallsyms檢視模組輸出的核心符號
3.模組依賴
如前所述,核心符號表記錄了所有模組可以訪問的符號及相應的位址。當乙個新的模組被裝入核心後,它所申明的某些符號就會被登記到這個表中,而這些符號可能被其他模組所引用,這就引出了模組依賴這個問題。
乙個模組a引用另乙個模組b所移出的符號,我們就說模組b被模組a引用,或者說模組a依賴模組b。如果要鏈結模組a,必須先鏈結模組b。這種模組間相互依賴的關係就叫模組依賴。
4.模組引用計數器
為 了確保模組安全地解除安裝,每個模組都有乙個引用計數器。當執行模組所涉及的操作時就遞增計數器,在操作結束時就遞減這個計數器;另外,當模組b被模組a引用 時,模組b的引用計數就遞增,引用結束,計數器遞減。什麼時候可以解除安裝這個模組?當然只有這個計數器值為0的時候,例如,當乙個檔案系統還被安裝在系統上 時就不能將其解除安裝,當這個檔案系統不再被使用時,引用計數器就為0,於是可以解除安裝。
四.模組編譯
linux 中最重要的軟體開發工具是 gcc。gcc 是 gnu 的 c 和 c++ 編譯器。但是,在大型的開發專案中,通常有幾十到上百個的原始檔,如果每次均手工鍵入 gcc 命令進行編譯的話,則會非常不方便。因此,人們通常利用 make 工具來自動完成編譯工作。利用這種自動編譯可大大簡化開發工作,避免不必要的重新編譯。這些工作包括:如果僅修改了某幾個原始檔,則只重新編譯這幾個原始檔;如果某個標頭檔案被修改了,則重新編譯所有包含該標頭檔案的原始檔。
1.編譯工具make
實際上,make 工具通過乙個稱為 makefile 的檔案來完成並自動維護編譯工作。makefile 需要按照某種語法進行編寫,其中說明了如何編譯各個原始檔並連線生成可執行檔案,並定義了原始檔之間的依賴關係。下面給出2.6 核心模組的makefile模板(請參看makefile的寫法)
# makefile2.6
obj-m += hellomod.o # 產生hellomod 模組的目標檔案
current_path := $(shell pwd) #模組所在的當前路徑
linux_kernel := $(shell uname -r) #linux核心源**的當前版本
linux_kernel_path := /usr/src/linux-headers-$(linux_kernel) #linux核心源**的絕對路徑
all:
make -c $(linux_kernel_path) m=$(current_path) modules #編譯模組了
clean:
make -c $(linux_kernel_path) m=$(current_path) clean #清理
注意: 在每個命令前(例如make命令前)要鍵入乙個製表符(按tab鍵產生)
有了makefile,執行make命令,會自動形成相關的字尾為.o和.ko檔案。
到此,模組編譯好了,該把它插入到核心了:
如:$insmod hellomod.ko
當然,要以系統員的身份才能把模組插入。
成功插入後,可以通過dmesg命令檢視,螢幕最後幾行的輸出就是你程式中輸出的內容:hello,world! from the kernel space…
當模組不再需要時,可以通過rmmod命令移去,例如
$rmmod hellomod
核心模組程式設計之入門(二) 必備知識
模組程式設計屬於核心程式設計,因此,除了對核心相關知識有所了解外,還需要了解與模組相關的知識。1 應用程式與核心模組的比較 為了加深對核心模組的了解,表一給出應用程式與核心模組程式的比較。表一 應用程式與核心模組程式的比較 c語言應用程式 核心模組程式 使用函式 libc庫 核心函式 執行空間 使用...
核心模組程式設計之入門(二) 必備知識
模組程式設計屬於核心程式設計,因此,除了對核心相關知識有所了解外,還需要了解與模組相關的知識。1 應用程式與核心模組的比較 為了加深對核心模組的了解,表一給出應用程式與核心模組程式的比較。表一 應用程式與核心模組程式的比較 c語言應用程式 核心模組程式 使用函式 libc庫 核心函式 執行空間 使用...
PL SQL程式設計入門之二
pl sql 的異常處理 根據exception 來進行異常處理 pl sql 的begin 和end 之間發生的異常通過在這個塊中定義的 exception 來處理。同一塊中沒有異常處理定義的情況下,則引用上層的意外處理。例如雖然捕捉了異常,但是在異常處理部分什麼也沒寫的情況下什麼也不會發生。但是...