Linux核心是一個整體是結構,因此向核心添加任何東西,或者刪除某些功能,都十分困難。為瞭解決這個問題引入了核心機制。從而可以動態想核心中添加或者刪除模組。
模組不被編譯在核心中,因而控制了核心的大小.然而模組一旦被插入核心,他就和核心其他部分一樣.這樣一來就會曾家一部分系統開銷。同時,如果模組出現問題,也許會帶來系統的崩潰。
模組的實現機制:
啟動時,由函數 void inti_modules() 來初始化模組,因為啟動事很多時候沒有模組.這個函數往往把核心自身當作一個虛模組。
如由系統需要,則調用一系列以sys 開頭的函數,對模組進行操作. 如:
sys_creat_modules(),sys_inti_modules() ,
sys_deldte_modules()等等.
這裡會用到一些模組的資料就結構,在/usr/scr/Linux/include/Linux/module.h 中,有興趣的朋友可以找出來一看塊的加入有兩種方法:一是手動加入:如:insmod modulename.另一種是根據需要,動態載入模組:如你執行命令:
$mount -t msdos /dev/hdd /mnt/d 時.系統便自動載入 FAT模組,以支援MSDOS的檔案系統。
1.模組編程
寫一個模組,必須有一定的多進程編程基礎,因為你變得程式不是以一個獨立的程式的來啟動並執行。另外,因為,模組需要在核心模式下運行,會遇到在內和空間和使用者空間資料交換的問題.一般的資料複製函數無法完成這一個過程。因此系統已入了一些特殊的函數以用來完成核心空間和使用者空間資料的交換/
這些函數有:void put _user (type valude,type *u_addr)
memcpy_tofs()
等等,有興趣的朋友可以仔細的看看所有的函數,以及他們的用法.需要說明的是.模組編程河核心的版本有很大的關係。如果版本不通可能造成,核心模組不能編譯,或者.在運行這個模組時,出現不可測結果。如:系統崩潰等。
明白了這些以後,你就可以嘗試著編寫核心模組了。對於每一個核心模組來說,必定包含兩個函數int init_module() 這個函數在插入核心時啟動,在核心中註冊一定的功能函數,或者用他的代碼代替內和中某些函數的內容(估計這些函數是空的)。因此,內和可以安全的卸載。
int cleanup_module() 當核心模組謝載時,調用.將模組從核心中清除.
同其他的程式設計教程一樣 ,我們給出一個hello world 的例子
/*hello.c a module programm*/
/* the program runing under kernel mod and it is a module*/
#include" Linux/kernerl.h"
#include"lLinux/module.h"
/* pross the CONFIG_MODVERSIONS*/
#if CONFIG_MODVERSIONS==1
#define MODVERSIONS
#include""Linux/modversions.h"
#end if
/* the init function*/
int init_module()
{
printk(" hello world !\n');
printd(" I have runing in a kerner");
return 1;
}
/* the distory function*/
int cleanup_module()
{
printk(" I will shut down myself in kernerl mod /n)";
retutn 0;
}
這樣一個例子就完成了.我們也寫一個makefile 的例子,以適於我們在大程式重的應用。一下是makfile 檔案的內容 。
# a makefile for a module
CC=gcc
MODCFLAGS:= -Wall _DMODULE -D_KERNEL_ -DLinux
hello.o hello.c /usr/inculde?Linux/version.h
CC $(MODCFLAGS) 0c hello.c
echo the module is complie completely
然後你運行make 命令 得到hello.o 這個模組,運行
$insmod hello.o
hello world!
I will shut down myself in kernerl mod
$lsmod
hello (unused)
….
$remmod
I will shut down myself in kernerl mod
這樣你的模組就可以隨意的插入和刪除了。
Linux中的大部分驅動程式,是以模組的形式編寫的,這些驅動程式源碼可以修改到核心中,也可以把他們編譯成模組形勢,在需要的時候動態載入。
一個典型的驅動程式,大體上可以分為這麼幾個部分:
1.註冊裝置
在系統初啟,或者模組載入時候,必須將裝置登記到相應的裝置數組,並返回裝置的主驅動號,例如:對快裝置來說調用 refister_blkdec()將裝置添加到數組blkdev中,並且獲得該裝置號,並利用這些裝置號對此數組進行索引。對於字元驅動裝置來說,要使用 module_register_chrdev()來獲得祝裝置的驅動號,然後對這個裝置的所有調用都用這個裝置號來實現。
2.定義功能函數
對於每一個驅動函數來說,都有一些和此裝置密切相關的功能函數,那最常用的塊裝置或者字元裝置來說,都存在著諸如 open() read() write() ioctrol()這一類的操作。當系統社用這些調用時,將自動的使用驅動函數中特定的模組,來實現具體的操作。而對於特定的裝置,上面的系統調用對應的函數是一定的。
如:在塊驅動裝置中.當系統試圖讀取這個裝置(即調用read()時),就會運行驅動程式中的block_read() 這個函數。
開啟新裝置時會調用這個裝置驅動程式的device_open() 這個函數.
3.謝載模組
在不用這個裝置時,可以將他卸載,主要是從/proc 中取消這個裝置的特殊檔案,可用特定的函數實現。
下面我們列舉一個字元裝置驅動程式的架構.來說明這個過程.
/* a module of a character device */
/* some include files*/
#include"param.h"
#include"user.h"
#include"tty.h"
#include"dir.h"
#include”fs.h"
/* the include files modules need*/
#include"Linux/kernel.h"
#include"Linux/module.h"
#if CONFIG_MODBERSIONS==1
degine MODBERSIONS
#include" Linux.modversions.h"
#endif
#difine devicename mydevice
/* the init funcion*/
int init_module()
{
int tag=module_register_chrdev(0,mydevice,&Fops);
if (tag<0)
{
printk("the device init is erro!\n");
return 1;
}
return 0;
}
/*the funcion which the device will be used */
int device_open ()
{
…….
}
int device_read ()
{
…….
}
int device_write ()
{
…….
}
int device_ioctl ()
{
…….
}
……
/* the deltter function of this module*/
int cleanup_module()
{
int re=module_unregister_chrdev(tag,mydevice);
if( re<0)
{
printk("erro unregister the module !!\n");
return 1;
}
return 0;
}