1. 程式人生 > >linux 驅動開發-模塊的構建

linux 驅動開發-模塊的構建

popu order ray 臨時 構建 大於 license 資源 編譯內核

1.模塊的含義

linux 是採用模塊化的方式構建的,同意內核在執行時動態地向當中插入或從中刪除代碼。這些代碼(包擴函數,數據,模塊入口函數,模塊出口函數)被一並組合

在一個單獨的二進制鏡像,就是所謂的可裝載內核模塊。

模塊能夠是主要的內核鏡像盡可能小,同一時候能夠方便地對新功能進行調試。還能夠實現熱插拔(興許會學習怎樣實現設備的熱插拔功能,臨時無需深究),和內核的核心子系統不一樣,模塊文件須要有入口點和出口點。

模塊與應用程序的差別:

a.模塊和庫函數相似,一個模塊通常包括若幹函數和數據,每一個函數提供特定的服務。決定完畢哪些功能則是應用程序的事情,模塊僅僅是用來為應用程序提供服務。也就是策略與機制的關系。模塊提供機制,應用程序依據模塊提供的機制實現策略。

b.模塊的註冊函數僅僅是把模塊註冊到內核中。模塊中的函數怎樣被調用是應用程序的事情。

c.應用程序退出是能夠無論資源的釋放和其它清理工作(通常我們還是會處理),但模塊的退出函數必須細致撤銷初始化函數所作的一切。

d.模塊被鏈接到內核,它僅僅能調用哪些被內核導出的函數(以後會講到怎樣導出內核函數),使用的頭文件是內核源碼中include/文件夾下的頭文件,如:

#include<linux/init.h>?? ======>include/linux/init.h

e.模塊不會和不論什麽庫鏈接

f.模塊編程必須謹慎考慮並發問題,通常必須是可重入的。

2.模塊的編譯,裝載和卸載。

請參考linux內核穩定。有很具體的描寫敘述。

Documentation/kbuild/modules.txt

由於文檔是英文。我簡單描寫敘述一下。

a.首先執行makefile編譯模塊,簡單分析make的編譯參數

make -C? 將文件夾改變到內核源碼所在文件夾,當中有內核的頂層makefile文件。

M=‘pwd’? 要求頂層makefile在構造modules目標之前返回到模塊的源碼文件夾。

modules 僅僅想obj-m變量中設定的模塊。就是告訴編譯系統,此make 是要編譯一個內核模塊。

內核轉載和卸載的命令

insmod
?46 通過insmod將模塊的代碼和數據安裝到系統正在執行的內核中。一旦成功安裝。則模塊就能夠訪問內核的公共符號(包括函數和變量)。

insmod和ld相似。但insmod不
??? 會改動模塊的磁盤文件,僅僅會改動內存中的副本。


rmmod
?通過rmmod卸載模塊。註意,僅僅有root權限才幹夠安裝和卸載模塊。

假設內核覺得模塊仍然處於使用狀態,有可能無法卸載。在編譯內核時須要將Loadable module s??? upport-->Module unloading選中,否則內核不支持模塊卸載。


lsmod

lsmod程序列出了當前裝載到內核中的所以模塊,還列出了其它信息,如其它模塊是否在使用某個特點模塊。lsmod通過讀取/proc/modules獲得這些信息。

上面的基本知識大家有個大概的印象就可以,以下演示樣例分析一下,一些程序學習的起源都是Hello world, 那麽我們也從Hello world開始。創建一個簡單的Hello world 模塊。


首先構建模塊編譯的makefile文件

#Makefile
obj-m = hello.o

KERN = /share/arm/linux-3.2                               #基於源碼樹的模塊編譯。源碼樹必須要先編譯完畢後,才幹進行模塊編譯
# KERN = /lib/modules/`uname -r`/build/                   ##假設你在x86平臺的linux系統,直接能夠編譯基於x86平臺的模塊

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

clean:
        make -C $(KERN) M=`pwd` modules clean
        rm -rf modules.order
看看首個模塊代碼helloWorld.c

#include <linux/init.h>
#include <linux/module.h>
//模塊必須的兩個頭文件
MODULE_LICENSE("GPL");      //GPL協議要求
MODULE_AUTHOR("MJ");   //option       描寫敘述作者信息
MODULE_DESCRIPTION("just for test");   //option  描寫敘述模塊的

static __init int hello_init(void)
{
    printk(KERN_EMERG,"hello kernel !\n");    //printk 相似應用程序的printf。 使用方法稍有不同,可百度一下。

} static __exit void hello_exit(void) { printk(KERN_EMERG,"bye, cruel world!\n"); } module_init(hello_init); module_exit(hello_exit);

通常printk不能直接輸出到終端,須要看終端的配置及你printk的打印機別。 可是一定會輸出到log文件,你能夠用dmesg 命令查看log message。

printk()
a.介紹
printk是內核中很好用的一種信息輸出方法。和printf很象。不同的是printk能夠附加不同的日誌級別,從而能夠依據消息的嚴重程度分類。

如:
printk(KERN_DEBUG "Here I am: %s:%i\n", __FILE__, __LINE__);

b.日誌級別
printk()定義在/kernel/printk.c中。消息級別和原型聲明都在<linux/kernel.h>中。

缺省級別在printk.c中指定。
支持的日誌級別:
KERN_EMERG: 緊急情況 ?
KERN_ALERT: 須要馬上被註意到的錯誤
KERN_CRIT: 臨界情況
KERN_ERR: 錯誤
KERN_WARNING: 警告
KERN_NOTICE: 註意
KERN_INFO: 非正式的消息
KERN_DEBUG: 調試信息(冗余信息)


c.printk向用戶空間的輸出
兩個守護進程syslogd和klogd用於處理日誌信息。小於控制臺級別的信息會被輸出到console。而大於控制臺級別的信息不輸出,僅僅是寫入/var/log/messages和/proc/kmsg中。

能夠通過dmesg查看。



改動控制臺級別:
$>echo 8 > /proc/sys/kernel/printk

$>klogd -c 8
/*須要先關閉klogd。用ps -C klogd 查出這個進程的pid,然後kill)

printk函數將消息寫到一個長度為__LOG_BUF_LEN字節的循環緩沖區中,然後喚醒不論什麽睡眠在syslog系統調用或正在讀取/proc/kmsg的進程。循環緩沖區滿了以後,會繞>回開始處填寫新的內容。

printk()能夠在中斷中調用。

當printk()的調用頻率過高時。還能夠通過printk_ratelimit()限制輸出。



(7)模塊的初始化和清除
<linux/init.h>中包括了和模塊的初始化和清除相關的信息。

初始化
a.module_init
用於註冊模塊的初始化函數
module_init(hello_init);

b.__init
用於修飾模塊的初始化函數。表明該函數僅僅在模塊的初始化期間使用。初始化完畢後,這個函數所占的內存會被釋放。


static int __init hello_init(void)
{
? ...
}

c.__initdata
用於修飾模塊的初始化數據結構。


int __initdata array[100];


清除
a.module_exit
用於註冊模塊的清除函數
module_exit(hello_exit);

b.__exit
static void __exit hello_exit(void)
{
? ...
清除函數沒有返回值,被__exit修飾的模塊僅僅能用於模塊卸載(編譯器會把該函數放在特殊的ELF段中)。其它時候調用清除函數都是錯的。


本文對linux 模塊進行主要的介紹。讓大家對linux模塊有個主要的認識。


下一篇文章將對模塊的其它特性進行解說


linux 驅動開發-模塊的構建