天天看點

linux核心子產品認識,linux核心子產品簡介

一、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");