linux 模块化编译,手把手教Linux驱动1-模块化编程 module
大家好,從本篇起,一口君將手把手教大家如何來(lái)學(xué)習(xí)Linux驅(qū)動(dòng),預(yù)計(jì)會(huì)有20篇關(guān)于驅(qū)動(dòng)初級(jí)部分知識(shí)點(diǎn)。本專題會(huì)一直更新,有任何疑問(wèn),可以留言或者加我微信。
Linux的開發(fā)者,遍布世界各地,他們相互之間覺(jué)大數(shù)估計(jì)都不認(rèn)識(shí)。如果真的是對(duì)這些開發(fā)者進(jìn)行統(tǒng)一管理,那是很難做到的。所以大牛們,在設(shè)計(jì)Linux內(nèi)核的時(shí)候,融入了模塊化的思想。也就是說(shuō),現(xiàn)在大家已經(jīng)有一個(gè)現(xiàn)成的Linux操作系統(tǒng)了,所有的開發(fā)者寫的代碼對(duì)于這個(gè)Linux操作系統(tǒng)而言都是一個(gè)模塊,開發(fā)者可以模塊的形式將自己的代碼添加到內(nèi)核,也可以從操作系統(tǒng)中卸載自己的模塊。這種思想,在實(shí)際的開發(fā)中特別別有用。
例如:在你的設(shè)備上已經(jīng)運(yùn)行了一個(gè)成熟的Limux操作系統(tǒng),由于客戶的需求變化,你需要向這個(gè)操作系統(tǒng)上添加一些功能。現(xiàn)在你有兩種做法:
第一種:獲得Linux源代碼,然后修改,添加功能,貌似挺牛,但是如果你寫的代碼不能一次性到達(dá)效果,你就必須去修改,這樣就每次必須重新編譯內(nèi)核,是不是很麻煩。最可怕的是你一不小心,把內(nèi)核源碼給修改錯(cuò)了,那該怎么辦呀?
第二種:快速編寫自己的功能代碼,然后以模塊的形式添加到Linux操作系統(tǒng)中,然后測(cè)試,發(fā)現(xiàn)不行,卸載模塊,繼續(xù)修改代碼,添加模塊(高富帥的干活方式),。。是不是比使用第一種方法的苦逼程序員要輕松很多呀!
大家需要注意的是,一般我們都是通過(guò)模塊化的方法向Linux操作系統(tǒng)添加驅(qū)動(dòng)程序,那些Linux核心的代碼,我個(gè)人覺(jué)得沒(méi)有幾個(gè)人會(huì)覺(jué)得不好,需要重新修改。
Linux 內(nèi)核模塊主要由以下幾個(gè)部分組成:
模塊加載函數(shù)(必須):當(dāng)通過(guò)insmod命令加載內(nèi)核模塊時(shí),模塊的加載函數(shù)會(huì)自動(dòng)被內(nèi)核執(zhí)行,完成本模塊相關(guān)初始化工作;
模塊卸載函數(shù)(必須):當(dāng)通過(guò)rmmod命令卸載模塊時(shí),模塊的卸載函數(shù)會(huì)自動(dòng)被內(nèi)核執(zhí)行,完成與模塊加載函數(shù)相反的功能;
模塊許可證聲明(必須):模塊許可證(LICENCE)聲明描述內(nèi)核模塊的許可權(quán)限,如果不聲明LICENCE,模塊被加載時(shí)將收到內(nèi)核被污染的警告。大多數(shù)
模塊參數(shù)(可選):模塊參數(shù)是模塊被加載的時(shí)候可以被傳遞給他的值,它本身對(duì)應(yīng)模塊內(nèi)部的全局變量;
模塊導(dǎo)出符號(hào)(可選):內(nèi)核模塊可以導(dǎo)出符號(hào)(symbol,對(duì)應(yīng)于函數(shù)或變量),這樣其他模塊可以使用本模塊中的變量或函數(shù);
模塊作者等信息聲明(可選)。
直接看代碼,no code no bb!
看完上面的代碼,請(qǐng)相信,你已經(jīng)對(duì)模塊化編程有了一個(gè)基本的認(rèn)識(shí)。上面這段代碼雖然很簡(jiǎn)單,但是他包含了Linux內(nèi)核模塊化編程需要的所有信息。
我們來(lái)一起總結(jié)一下Linux內(nèi)核模塊化編程必備的步驟:
??包含linux/init.h和inux/module.h這兩個(gè)頭文件;通過(guò)MODULE LICENSE("GPL"),告訴內(nèi)核你的模塊遵從"GPL"協(xié)議,這個(gè)事情必須得做。如果不知道GPL的讀者自己去查找相關(guān)資料;Linux能夠成功一個(gè)關(guān)鍵因素就是遵循了GPL,從而一發(fā)不可收拾,在全球蔓延開來(lái)。
MODULE_AUTHOR("yikoulinux")用來(lái)指定編寫這個(gè)模塊的作者,可以不寫。
編寫功能代碼,注意這里沒(méi)有main函數(shù),這里叫模塊的入口函數(shù),函數(shù)名一般叫“xxx_init”,這里叫"hello_init"。名字是不是一定要好了,你可以把模塊的入口函數(shù)當(dāng)做你的main兩數(shù),你的代碼就從這個(gè)地方起步吧!
那這個(gè)函數(shù)什么時(shí)候被調(diào)用呢?在模塊加載到Linux內(nèi)核的時(shí)候,Linux內(nèi)核會(huì)調(diào)用這個(gè)函數(shù)模塊的退出函數(shù),這個(gè)函數(shù)的名字一般叫“xxx_exit”,這里叫“hello_exit”。這個(gè)函數(shù)里,我們一般會(huì)做些資源的釋放。在模塊卸載的時(shí)候會(huì)被調(diào)用到。當(dāng)一個(gè)模塊卸載的時(shí)候,我們肯定要把它占用的資源釋放掉,不然不就造成資源浪費(fèi)了。
告訴內(nèi)核,你的模塊入入口和模塊出口。Linux內(nèi)核提供了兩個(gè)宏,分別是:module_init 和 module_exit.
下面我們就來(lái)詳細(xì)說(shuō)一下printk函數(shù)。
printk的用法和printf類似,print用于用戶空間,printk用于內(nèi)核空間。用printk函數(shù)時(shí),內(nèi)核會(huì)根據(jù)日志級(jí)別,可能把消息打印到當(dāng)前控制臺(tái)上,這個(gè)控制臺(tái)通常是一個(gè)字符模式的終端、一個(gè)串口打印機(jī)或是一個(gè)并口打印機(jī)。
這些消息正常輸出的前提是:日志輸出級(jí)別小于console_loglevel(在內(nèi)核中數(shù)字越小優(yōu)先級(jí)越高)。
日志級(jí)別一共有8個(gè)級(jí)別,printk的日志級(jí)別定義如下(在include/linux/kern_levels.h中);
#define?KERN_EMERG?KERN_SOH?"0"?/system?is?unusable/#define?KERN_ALERT?KERN_SOH?"1"?/action?must?be?taken?immediately/#define?KERN_CRIT?KERN_SOH?"2"?/critical?conditions/#define?KERN_ERR?KERN_SOH?"3"?/error?conditions/#define?KERN_WARNING?KERN_SOH?"4"?/warning?conditions/#define?KERN_NOTICE?KERN_SOH?"5"?/normal?but?significant?condition/#define?KERN_INFO?KERN_SOH?"6"?/informational/#define?KERN_DEBUG?KERN_SOH?"7"?/debug-level?messages/#define?KERN_DEFAULT?KERN_SOH?"d"?/the?default?kernel?loglevel/沒(méi)有指定日志級(jí)別的printk語(yǔ)句默認(rèn)采用的級(jí)別是DEFAULT_MESSAGE_LOGLEVEL(這個(gè)默認(rèn)級(jí)別一般為<4>,即與KERN_WARNING在一個(gè)級(jí)別上)。
我們可以通過(guò)cat/proc/sys/kemel/printk這個(gè)文件,查看系統(tǒng)默認(rèn)的日志級(jí)別
printk,其實(shí)不用想那么復(fù)雜,你就把它當(dāng)做printf使用也可以的,在這里我們還不能測(cè)試printk輸出的消息,是否能到控制臺(tái)上,因?yàn)槲覀儾恢廊绾尉幾g我們的模塊代碼、如何加載我們的模塊、如何卸載我們的模塊。
好,接下來(lái)我們來(lái)看看,如何編譯我們的模塊。
這里,先給出Linux模塊化編譯的流程:
模塊的編譯分兩步:
第一步:調(diào)用linux源碼樹的Makefile進(jìn)行收集編譯一個(gè)模塊所需要的信息
第二步:linux源碼樹的Makefile在收集完信息后,調(diào)用模塊的Makefile。獲取需要編譯成模塊的“.c”文件,最后生成模塊文件
明白了模塊的編譯流程,接下來(lái)我們就來(lái)看具體如何編寫模塊的Makefile
#?KERNELRELEASE?:在內(nèi)核源碼樹的Makefile中定義,在當(dāng)前的Makefile中,它的值為空#(shell?pwd)獲得當(dāng)前路徑
在這里插入圖片描述
我們來(lái)分析Makefile的執(zhí)行過(guò)程
在模塊的源代碼目錄下執(zhí)行make,此時(shí),宏“KERNELRELEASE”【內(nèi)核源碼樹的Makefile會(huì)定義】沒(méi)有定義,因此進(jìn)入else;
記錄內(nèi)核路徑KDIR和當(dāng)前工作目錄PWD;
由于make 后面沒(méi)有目標(biāo),所以make會(huì)在Makefile中的第一個(gè)不是以.開頭的目標(biāo)作為默認(rèn)的目標(biāo)執(zhí)行,于是all成為make的目標(biāo);
all:的第一個(gè)命令
類似于printf函數(shù),編譯經(jīng)過(guò)此處會(huì)打印提示信息。
make的第二條命令會(huì)執(zhí)行 make -C (PWD) modules
翻譯下就是:
之所以這么寫是由內(nèi)核源碼樹的頂層Makefile告訴我們的,當(dāng)我們調(diào)用Linux內(nèi)核源碼樹頂層的Makefile時(shí),找到的是頂層Makefile的“modules”目標(biāo)。我們來(lái)看下頂層Makefile的modules目標(biāo)寫了什么:
在這里插入圖片描述
【截取了部分內(nèi)容,我們沒(méi)有必要全部了解,只需要關(guān)心紅色部分即可,特別是對(duì)應(yīng)的英文注釋】
找到modules目標(biāo)后,接下來(lái)Linux源碼樹的頂層Makeflle就需要知道是將那些".c"文件編譯成模塊。誰(shuí)告訴它呢?是的,模塊的Makefile文件。所以接下來(lái)就會(huì)回調(diào)模塊的Makefile。需要注意的是,此時(shí)KERNELRELEASE已經(jīng)在Linux內(nèi)核源碼樹的頂層Makefile中定義過(guò)了,所以此時(shí)它獲得信息是:
obj-m:=hello.o
obj-m表示會(huì)將hello.o目標(biāo)編譯成.ko模塊;它告訴linux源碼樹頂層Makefile是動(dòng)態(tài)編譯(編譯成模塊)而不是編譯進(jìn)內(nèi)核(obj-y),linux源碼樹頂層Makefile會(huì)根據(jù)hello.o找到hello.c文件
將模塊文件hello.c編譯為.o,然后再將多個(gè)目標(biāo)鏈接為.ko。
最終編譯結(jié)果如下:
由執(zhí)行結(jié)果可知,Makefile最終被調(diào)用了三次
1) 執(zhí)行命令make調(diào)用
2) 被linux內(nèi)核源碼樹的頂層Makefile調(diào)用,產(chǎn)生.o文件
3) 被linux源碼樹頂層Makefile調(diào)用,將.o文件鏈接生成.ko文件
如何將編譯好的模塊添加到Linux內(nèi)核?如何從Linux內(nèi)核將我們的模塊卸載下來(lái)?
1.模塊的加載命令
insmod?xxx.ko例如:在ubuntu系統(tǒng)中添加自己寫的模塊
sudo?insmod?hello.ko注意:在Linux系統(tǒng)中只有超級(jí)用戶權(quán)限才可以添加模塊到內(nèi)核。
2.查看系統(tǒng)中的模塊命令
lsmod例如:在系統(tǒng)中搜索自己添加的hello模塊
sudo?lsmod?|?grep?hello3.卸載模塊命令
sudo?rmmod?模塊名例如:卸載系統(tǒng)中的hello模塊
sudo?mmod?hello4.查看加載模塊和卸載模塊通過(guò)printk打印的信息命令
dmesg或dmesg|tail這個(gè)命令主要是從Linux內(nèi)核的ring buffer(環(huán)形緩沖區(qū))中讀取信息的。
那什么是ring buffer呢?在Limux系統(tǒng)中,所有通過(guò)printk打印出來(lái)的信息都會(huì)送到ring buffer中。我們知道,我們打印出來(lái)的信息是需要在控制臺(tái)設(shè)備上顯示的。在Linux內(nèi)核初始化的時(shí)候,控制臺(tái)設(shè)備并沒(méi)有初始化的時(shí)候,使用printk會(huì)不會(huì)有問(wèn)題
控制臺(tái)設(shè)備,因?yàn)榇藭r(shí)printk只是把信息輸送到ring buffer中,等控制臺(tái)設(shè)備初始化好后,在根據(jù)ring buffer中消息的優(yōu)先級(jí)決定是否需要輸送到控制臺(tái)設(shè)備上。
如何清空ring buffer呢?
?sudo?dmesg?-c操作結(jié)果如下:
一口君操作全部在特權(quán)模式下,如果在普通用戶權(quán)限下前面加sudo。
更多嵌入式資料,請(qǐng)關(guān)注公眾號(hào): 一口Linux。
總結(jié)
以上是生活随笔為你收集整理的linux 模块化编译,手把手教Linux驱动1-模块化编程 module的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: linux nslookup脚本,Lin
- 下一篇: linux 虚拟机安装图形界面,linu