一、linux核心子產品簡介
linux核心整體結構非常龐大,其包含的元件也非常多。我們怎麼把需要的部分都包含在核心中呢?
一種辦法是把所有的需要的功能都編譯到核心中。這會導緻兩個問題,一是生成的核心會很大,二是如果我們要在現有的核心中新增或删除功能,不得不重新編譯核心,工作效率會非常的低,同時如果編譯的子產品不是很完善,很有可能會造成核心崩潰。
linux提供了另一種機制來解決這個問題,這種集中被稱為子產品,可以實作編譯出的核心本身并不含有所有功能,而在這些功能需要被使用的時候,其對應的代碼可以被動态的加載到核心中。
二、子產品特點:
1)子產品本身并不被編譯入核心,進而控制了核心的大小。
2)子產品一旦被加載,他就和核心中的其他部分完全一樣。
注意:子產品并不是驅動的必要形式:即:驅動不一定必須是子產品,有些驅動是直接編譯進核心的;同時子產品也不全是驅動,例如我們寫的一些很小的算法可以作為子產品編譯進核心,但它并不是驅動。就像燒餅不一定是圓的,圓的也不都是燒餅一樣。
三、最簡單的子產品分析
1)以下是一個最簡單的子產品例子
#include
#include
staticint__init hello_init(void)
{
printk(KERN_INFO" Hello World enter\n");
return0;
}
staticvoid__exit hello_exit(void)
{
printk(KERN_INFO" Hello World exit\n ");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A simple Hello World Module");
MODULE_ALIAS("a simplest module");
#include
#include
static int __init hello_init(void)
{
printk(KERN_INFO " Hello World enter\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_INFO " Hello World exit\n ");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A simple Hello World Module");
MODULE_ALIAS("a simplest module");
2) 以下是編譯上述子產品所需的編寫的makefile
obj-m :=hello.o//目标檔案
#module-objs := file1.o file.o //當子產品有多個檔案組成時,添加本句
KDIR :=/usr/src/linux//核心路徑,根據實際情況換成自己的核心路徑,嵌入式的換成嵌入式,PC機的指定PC機路徑
PWD := $(shell pwd)//子產品源檔案路徑
all:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
@rm -rf *.mod.*
@rm -rf .*.cmd
@rm -rf *.o
@rm -rf Module.*
clean:
rm -rf *.ko
obj-m :=hello.o //目标檔案
#module-objs := file1.o file.o //當子產品有多個檔案組成時,添加本句
KDIR :=/usr/src/linux //核心路徑,根據實際情況換成自己的核心路徑,嵌入式的換成嵌入式,PC機的指定PC機路徑
PWD := $(shell pwd) //子產品源檔案路徑
all:
$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
@rm -rf *.mod.*
@rm -rf .*.cmd
@rm -rf *.o
@rm -rf Module.*
clean:
rm -rf *.ko
最終會編譯得到:hello.ko檔案
使用insmodhello.ko将子產品插入核心,然後使用dmesg即可看到輸出提示資訊。
常用的幾種子產品操作:
insmod XXX.ko 加載指定子產品
lsmod 列舉目前系統中的所有子產品
rmmod XXX 解除安裝指定子產品(注意沒有.ko字尾)
dmesg 當列印等級低于預設輸出等級時,采用此指令檢視系統日志
3)linux核心子產品的程式結構
1.子產品加載函數:
Linux核心子產品一般以__init标示聲明,典型的子產品加載函數的形式如下:
staticint__init myModule_init(void)
{
PRINTK("myModule_init\n");
return0;
}
module_init(myModule_init);
static int __init myModule_init(void)
{
PRINTK("myModule_init\n");
return 0;
}
module_init(myModule_init);
子產品加載函數的名字可以随便取,但必須以“module_init(函數名)”的形式被指定;
執行insmod指令時被執行,用于初始化子產品所必需資源,比如記憶體空間、硬體裝置等;
它傳回整形值,若初始化成功,應傳回0,初始化失敗傳回負數。
2.子產品解除安裝函數
典型的子產品解除安裝函數形式如下:
staticvoid__exit myModule_exit(void)
{
PRINTK("myModule_exit\n");
return;
}
module_exit(myModule_exit);
static void __exit myModule_exit(void)
{
PRINTK("myModule_exit\n");
return;
}
module_exit(myModule_exit);
子產品解除安裝函數在子產品解除安裝的時候執行,不傳回任何值,需用”module_exit(函數名)”的形式被指定。
解除安裝子產品完成與加載函數相反的功能:
若加載函數注冊了XXX,則解除安裝函數應當登出XXX
若加載函數申請了記憶體空間,則解除安裝函數應當釋放相應的記憶體空間
若加載函數申請了某些硬體資源(中斷、DMA、I/0端口、I/O記憶體等),則解除安裝函數應當釋放相應的硬體資源
若加載函數開啟了硬體,則解除安裝函數應當關閉硬體。
其中__init 、__exit 為系統提供的兩種宏,表示其所修飾的函數在調用完成後會自動回收記憶體,即核心認為這種函數隻會被執行1次,然後他所占用的資源就會被釋放。
3.子產品聲明與描述
在linux核心子產品中,我們可以用MODULE_AUTHOR、MODULE_DESCRIPTION、MODULE_VERSION、MODULE_TABLE、MODULE_ALIA,分别描述子產品的作者、描述、版本、裝置表号、别名等。
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A simple Hello World Module");
MODULE_ALIAS("a simplest module");
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("A simple Hello World Module");
MODULE_ALIAS("a simplest module");
四、有關子產品的其它特性
1)子產品參數:
我們可以利用module_param(參數名、參數類型、參數讀寫屬性) 為子產品定義一個參數,例如:
staticchar*string_test = “thisis a test”;
staticnum_test = 1000;
module_param (num_test,int,S_IRUGO);
module_param (steing_test,charp,S_ITUGO);
static char *string_test = “this is a test”;
static num_test = 1000;
module_param (num_test,int,S_IRUGO);
module_param (steing_test,charp,S_ITUGO);
在裝載子產品時,使用者可以給子產品傳遞參數,形式為:”insmod 子產品名 參數名=參數值”,如果不傳遞,則參數使用預設的參數值
參數的類型可以是:byte,short,ushort,int,uint,long,ulong,charp,bool;
權限:定義在linux/stat.h中,控制存取權限,S_IRUGO表示所有使用者隻讀;
子產品被加載後,在sys/module/下會出現以此子產品命名的目錄,當讀寫權限為零時:表示此參數不存在sysfs檔案系統下的檔案節點,當讀寫權限不為零時:此子產品的目錄下會存在parameters目錄,包含一系列以參數名命名的檔案節點,這些檔案節點的權限值就是傳入module_param()的“參數讀/寫權限“,而該檔案的内容為參數的值。
除此之外,子產品也可以擁有參數數組,形式為:”module_param_array(數組名、數組類型、數組長、參數讀寫權限等)”,當不需要儲存實際的輸入的數組元素的個數時,可以設定“數組長“為0。
運作insmod時,使用逗号分隔輸入的數組元素。
下面是一個實際的例子,來說明子產品傳參的過程。
#include
#include
#include
#define DEBUG //open debug message
#ifdef DEBUG
#define PRINTK(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#endif
staticchar*string_test="default paramater";
staticintnum_test=1000;
staticint__init hello_init(void)
{
PRINTK("\nthe string_test is : %s\n",string_test);
PRINTK("the num_test is : %d\n",num_test);
return0;
}
staticvoid__exit hello_exit(void)
{
PRINTK(" input paramater module exit\n ");
}
module_init(hello_init);
module_exit(hello_exit);
module_param(num_test,int,S_IRUGO);
module_param(string_test,charp,S_IRUGO);
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("GPL");
#include
#include
#include
#define DEBUG//open debug message
#ifdef DEBUG
#define PRINTK(fmt, arg...)printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...)printk(KERN_DEBUG fmt, ##arg)
#endif
static char *string_test="default paramater";
static int num_test=1000;
static int __init hello_init(void)
{
PRINTK("\nthe string_test is : %s\n",string_test);
PRINTK("the num_test is : %d\n",num_test);
return 0;
}
static void __exit hello_exit(void)
{
PRINTK(" input paramater module exit\n ");
}
module_init(hello_init);
module_exit(hello_exit);
module_param(num_test,int,S_IRUGO);
module_param(string_test,charp,S_IRUGO);
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("GPL");
當執行 insmod hello_param.ko時,執行dmesg 檢視核心輸出資訊:
Hello World enter
the test string is:thisis a test
the test num is :1000
Hello World enter
the test string is: this is a test
the test num is :1000
當執行insmod hello_param.ko num_test=2000 string_test=“edit by dengwei”,執行dmesg檢視核心輸出資訊:
Hello World enter
the test string is: edit by dengwei
the test num is :2000
Hello World enter
the test string is: edit by dengwei
the test num is :2000
2)導出子產品及符号的互相引用
Linux2.6核心的“/proc/kallsyms“檔案對應核心符号表,它記錄了符号以及符号所在的記憶體位址,子產品可以使用下列宏導到核心符号表中。
EXPORT_SYMBOL(符号名); 任意子產品均可
EXPORT_SYMBOL_GPL(符号名); 隻使用于包含GPL許可權的子產品
導出的符号可以被其它子產品使用,使用前聲明一下即可。
下面給出一個簡單的例子:将add sub符号導出到核心符号表中,這樣其它的子產品就可以利用其中的函數
#include
#include
#include
intadd_test(inta ,intb)
{
returna + b;
}
intsub_test(inta,intb)
{
returna - b;
}
EXPORT_SYMBOL(add_test);
EXPORT_SYMBOL(sub_test);
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("GPL");
#include
#include
#include
int add_test(int a ,int b)
{
return a + b;
}
int sub_test(int a,int b)
{
return a - b;
}
EXPORT_SYMBOL(add_test);
EXPORT_SYMBOL(sub_test);
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("GPL");
執行 cat/proc/kallsyms | grep test 即可找到以下資訊,表示子產品确實被加載到核心表中。
f88c9008 r __ksymtab_sub_integar [export_symb]
f88c9020 r __kstrtab_sub_integar [export_symb]
f88c9018 r __kcrctab_sub_integar [export_symb]
f88c9010 r __ksymtab_add_integar [export_symb]
f88c902c r __kstrtab_add_integar [export_symb]
f88c901c r __kcrctab_add_integar [export_symb]
f88c9000 T add_tes [export_symb]
f88c9004 T sub_tes [export_symb]
13db98c9 a __crc_sub_integar [export_symb]
e1626dee a __crc_add_integar [export_symb]
f88c9008 r __ksymtab_sub_integar [export_symb]
f88c9020 r __kstrtab_sub_integar [export_symb]
f88c9018 r __kcrctab_sub_integar [export_symb]
f88c9010 r __ksymtab_add_integar [export_symb]
f88c902c r __kstrtab_add_integar [export_symb]
f88c901c r __kcrctab_add_integar [export_symb]
f88c9000 T add_tes [export_symb]
f88c9004 T sub_tes [export_symb]
13db98c9 a __crc_sub_integar [export_symb]
e1626dee a __crc_add_integar [export_symb]
在其它子產品中可以引用此符号
#include
#include
#include
#define DEBUG //open debug message
#ifdef DEBUG
#define PRINTK(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#endif
externintadd_test(inta ,intb);
externintsub_test(inta,intb);
staticint__init hello_init(void)
{
inta,b;
a = add_test(10,20);
b = sub_test(30,20);
PRINTK("the add test result is %d",a);
PRINTK("the sub test result is %d\n",b);
return0;
}
staticvoid__exit hello_exit(void)
{
PRINTK(" Hello World exit\n ");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_AUTHOR("dengwei");
MODULE_LICENSE("GPL");