嵌入式linux驅動之——內核模塊

一、簡介:

Linux內核是一個整體結構,但是通過內核模塊的方式向開發人員提供了一種動態加載程序到內核的能力。通過內核模塊,開發人員可以訪問內核的資源,內核還向開發人員提供了訪問底層硬件和總線的接口。因此,Linux系統的驅動是通過內核模塊實現的

Linux內核模塊是一種可以被內核動態加載和卸載的可執行程序。通過內核模塊可以擴展內核的功能,通常內核模塊被用於設備驅動、文件系統等。如果沒有內核模塊,需要向內核添加功能就需要修改代碼、重新編譯內核、安裝新內核等步驟,不僅繁瑣,而且容易出錯,不易於調試。

 

二、常用的模塊操作命令:

(1)lsmod(list module,將模塊列表顯示),功能是打印出當前內核中已經安裝的模塊列表

(2)insmod(install module,安裝模塊),功能是向當前內核中去安裝一個模塊,用法是insmod xxx.ko

(3)modinfo(module information,模塊信息),功能是打印出一個內核模塊的自帶信息。

(4)rmmod(remove module,卸載模塊),功能是從當前內核中卸載一個已經安裝了的模塊,用法是rmmod xxx(注意卸載模塊時只需要輸入模塊名即可,不能加.ko後綴)

三、編寫一個最簡單的的內核模塊

module_test.c

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit

// 模塊安裝函數
static int __init chrdev_init(void)
{	
	printk(KERN_INFO "chrdev_init helloworld init\n");
	return 0;
}

// 模塊卸載函數
static void __exit chrdev_exit(void)
{
	printk(KERN_INFO "chrdev_exit helloworld exit\n");
}


module_init(chrdev_init);//註冊模塊的初始化函數
module_exit(chrdev_exit);//註冊模塊的退出函數

// MODULE_xxx這種宏作用是用來添加模塊描述信息
MODULE_LICENSE("GPL");				// 描述模塊的許可證
MODULE_AUTHOR("liyijun");				// 描述模塊的作者
MODULE_DESCRIPTION("module test");	// 描述模塊的介紹信息
MODULE_ALIAS("clark");			// 描述模塊的別名信息

module_init的通俗理解:

模塊源代碼中用module_init宏聲明瞭一個函數(在我們這個例子裏是chrdev_init函數),作用就是指定chrdev_init這個函數和insmod命令綁定起來,也就是說當我們insmod module_test.ko時,insmod命令內部實際執行的操作就是幫我們調用chrdev_init函數。模塊安裝時insmod內部除了幫我們調用module_init宏所聲明的函數外,實際還做了一些別的事(譬如lsmod能看到多了一個模塊也是insmod幫我們在內部做了記錄),但是我們就不用管了。

module_init的深入理解:

http://www.seotest.cn/jishu/34634.html

https://www.cnblogs.com/sky-heaven/p/10344826.html

https://blog.csdn.net/anglexuchao/article/details/80391928

https://www.jianshu.com/p/a783474efb44

 

四、編譯內核模塊

 Makefile

#ubuntu的內核源碼樹,如果要編譯在ubuntu中安裝的模塊就打開這2個
KERN_VER = $(shell uname -r)
KERN_DIR = /lib/modules/$(KERN_VER)/build	

obj-m	+= module_test.o

all:
	make -C $(KERN_DIR) M=`pwd` modules 


.PHONY: clean	
clean:
	make -C $(KERN_DIR) M=`pwd` modules clean

 

Makefile解釋:

1、uname -r : 顯示操作系統的發行版號,所以在我的計算機上KERN_VER變量就是4.15.0-64-generic

KERN_DIR 變量就是 /lib/modules/4.4.0-62-generic/build    

2、obj-m += module_test.o,這一行就表示我們要將module_test.c文件編譯成一個模塊 ,不會編譯到內核,但是會生成一個獨立的 "module_test.ko" 文件;

假如是obj-y則表示把module_test.o文件編譯進內核;

3、make -C $(KERN_DIR) M=`pwd` modules      該命令是make modules命令的擴展,用來實際編譯模塊,大致意思就是:利用make -C(進入到某個目錄下編譯)進入到我們指定的內核源碼樹目錄下,然後在源碼目錄樹下借用內核源碼中定義的模塊編譯規則(makefile)去編譯這個模塊,M=`pwd`表明然後返回到當前目錄繼續讀入、執行當前的Makefile。

https://www.cnblogs.com/liulipeng/archive/2013/11/05/3408238.html

編譯完成之後,生成module_test.ko和幾個臨時文件:

 

五、安裝與卸載內核模塊

在執行完安裝命令後並沒有將chrdev_init helloworld init信息打印,這是因爲ubuntu中這個printk的打印級別控制沒法實踐,ubuntu中不管你把級別怎麼設置都不能直接打印出來,必須dmesg命令去查看。

關於tail命令參考以下:

https://www.runoob.com/linux/linux-comm-tail.html

我們對模塊進行卸載:

 

六、內核模塊的傳參

驅動程序常需要在加載的時候提供一個或者多個參數,內模塊提供了設置參數的能力。通過module_param()宏可以爲內核模塊設置一個參數。定義如下:
module_param(參數名稱,類型,屬性)其中,參數名稱是加載內核模塊時使用的參數名稱,在內核模塊中需要有一個同名的變量與之對應;類型是參數的類型,內核支持C語言常用的基本類型;屬性是參數的訪問權限。

代碼示例:

#include <linux/module.h>		// module_init  module_exit
#include <linux/init.h>			// __init   __exit
#include <linux/moduleparam.h>	

static int initValue = 0;
static char* initName = NULL;

module_param(initValue,int,S_IRUGO);
module_param(initName,charp,S_IRUGO);
// 模塊安裝函數
static int __init chrdev_init(void)
{	
	printk(KERN_INFO "initValue = %d,initName = %s\n",initValue,initName);
	printk(KERN_INFO "chrdev_init helloworld init\n");
	return 0;
}

// 模塊卸載函數
static void __exit chrdev_exit(void)
{
	printk(KERN_INFO "chrdev_exit helloworld exit\n");
}


module_init(chrdev_init);//註冊模塊的初始化函數
module_exit(chrdev_exit);//註冊模塊的退出函數

// MODULE_xxx這種宏作用是用來添加模塊描述信息
MODULE_LICENSE("GPL");				// 描述模塊的許可證
MODULE_AUTHOR("liyijun");				// 描述模塊的作者
MODULE_DESCRIPTION("module test");	// 描述模塊的介紹信息
MODULE_ALIAS("clark");			// 描述模塊的別名信息

 Makefile文件同上

安裝模塊的時候一同傳遞參數:

使用modinfo命令查看內核模塊的信息:

REF:

朱友鵬課件

《ARM嵌入式linux系統開發詳解》

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