ARM學習筆記之驅動程序篇三----內核模塊

1.6 內核模塊

1.6.1 內核模塊基礎知識

     內核模塊是Linux內核向外部提供的一個插口,其全稱爲動態可加載內核模塊(Loadable Kernel Module,LKM),我們簡稱爲模塊。Linux內核之所以提供模塊機制,是因爲它本身是一個單內核(monolithic kernel)。單內核的最大優點是效率高,因爲所有的內容都集成在一起,但其缺點是可擴展性和可維護性相對較差,模塊機制就是爲了彌補這一缺陷。模塊是具有獨立功能的程序,它可以被單獨編譯,但不能獨立運行。它在運行時被鏈接到內核作爲內核的一部分在內核空間運行,這與運行在用戶空間的進程是不同的。模塊通常由一組函數和數據結構組成,用來實現一種文件系統、一個驅動程序或其他內核上層的功能。總之,模塊是一個爲內核(從某種意義上來說,內核也是一個模塊)或其他內核模塊提供使用功能的代碼塊。

1,內核模塊的特點

(1)模塊本身並不被編譯進內核文件(zImage或者bzImage)

(2)可以根據需求,在內核運行期間動態的安裝或者卸載。

2,安裝/卸載內核模塊

(1)安裝insmod

    例:insmod dnw_usb.ko

(2)卸載rmmod

    例:rmmod dnw_usb.ko

(3)查看 lsmod

    例:lsmod

1.6.2 內核模塊的設計

1,內核模塊範例

#include<linux/init.h>
#include<linux/module.h>
static int hello_init(){
    printk(KERN_WARNING"hello world!\n");
    return 0;
}

static int hello_exit(){
    printk(KERN_WARNING"hell exit!\n");
    return 0;
}

module_init(hello_init);
module_exit(hello_exit);

內核通過printk()輸出的信息具有日誌級別,日誌級別是通過在printk()輸出的字符串前加一個帶尖括號的整數來控制的,如printk("<6>Hello, world!\n");。內核中共提供了八種不同的日誌級別,在 linux/kernel.h 中有相應的宏對應。

#define KERN_EMERG   "<0>"   /* system is unusable */

#define KERN_ALERT   "<1>"   /* action must be taken immediately */

#define KERN_CRIT     "<2>"   /* critical conditions */

#define KERN_ERR      "<3>"   /* error conditions */

#define KERN_WARNING "<4>"   /* warning conditions */

#define KERN_NOTICE   "<5>"   /* normal but significant */

#define KERN_INFO     "<6>"   /* informational */

#define KERN_DEBUG    "<7>"   /* debug-level messages */

KERN_EMERG

用於突發性事件的消息,通常在系統崩潰之前報告此類消息。 

KERN_ALERT

在需要立即操作的情況下使用此消息。 

KERN_CRIT

用於臨界條件下,通常遇到嚴重的硬軟件錯誤時使用此消息。 

KERN_ERR

用於報告錯誤條件;設備驅動經常使用KERN_ERR報告硬件難題。

KERN_WARNING

是關於問題狀況的警告,一般這些狀況不會引起系統的嚴重問題。 

KERN_NOTICE

該級別較爲普通,但仍然值得注意。許多與安全性相關的情況會在這個級別被報告。 

KERN_INFO

信息消息。許多驅動程序在啓動時刻用它來輸出獲得的硬件信息。

KERN_DEBUG

用於輸出調試信息。

2.編寫Makefile

obj-m:=helloworld.o
KDIR:=/home/win/Tiny6410/linux-tiny6410
all:
 make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux-ARCH=arm
clean:
 rm -f *.o,*.ko,*.order,*.symvers

obj-m := helloworld.o爲固定格式,指明內核模塊的名字,如該模塊有多個文件組成則須在下面加一行helloworld-objs := file1.o file2.o;KDIR := /home/win/Tiny6410/linux-tiny6410指明內核代碼位置;make -C $(KDIR) M=$(PWD) modules CROSS_COMPILE=arm-linux- ARCH=arm,-C進入到內核代碼中,M指明內核模塊的位置。 

1.6.3 內核模塊可選信息

1,模塊申明

(1)申明該模塊遵守的許可證協議,如:“GPL”,“GPL v2”等

     MODULE_LICENSE("GPL")

(2)申明模塊的作者

     MODULE_AUTHOR("JON")

(3)申明模塊的功能

    MODULE_DESCRIPTION("描述模塊的功能")

(4)申明模塊的版本

    MODULE_VERSION("V1.0")

2,模塊參數

在應用程序中int main(int argc, char** argv)。argc表示命令行輸入的參數個數,argv中保存輸入的參數。通過宏module_param指定保存模塊參數的變量。模塊參數用於在加載模塊時傳遞參數給模塊。

module_param(name,type,perm);

說明:

name:變量的名稱

type:變量類型,bool:布爾型 int:整型 charp:字符串型

perm:是訪問權限。S_IRUGO:讀權限 S_IWUSR:寫權限

3,符號導出

如果內核模塊中的變量或函數等要提供給其他內核模塊使用,必須使用宏EXPORT_SYMBOL(或者EXPORT_SYMBOL_GPL)將符號導出。

EXPORT_SYMBOL(符號名)

EXPORT_SYMBOL_GPL(符號名)

說明:其中EXPORT_SYMBOL_GPL只能用於包含GPL許可證的模塊。

//helloworld.c
#include<linux/init.h>
#include<linux/module.h>
extern int add(int a,int b);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("hello world");
MODULE_VERSION("V1.0");
int a=3;

module_param(a,int,S_IRUGO|S_IWUSR);

static int hello_init(){
    printk(KERN_WARNING"hello world!\n");
    printk("a=%d\n",a);
    return 0;
}

static int hello_exit(){
    int i;
    i=add(3,4);
    printk("3+4=%d\n");
    printk(KERN_WARNING"hello exit!\n");
    return 0;
}

module_init(hello_init);
module_exit(hello_exit);

//add.c
#include<linux/init.h>
#include<linux/module.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("hello world");
MODULE_VERSION("V1.0");
int add(int a,int b){
    return (a+b);
}

static int add_init(){
    return 0;
}

static int add_exit(){
    return 0;
}

EXPORT_SYMBOL(add);
module_init(add_init);
module_exit(add_exit);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章