日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

LINUX设备驱动程序的注意事项(两)建设和执行模块

發(fā)布時(shí)間:2025/4/5 linux 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 LINUX设备驱动程序的注意事项(两)建设和执行模块 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

? ? ? ? ?<>:設(shè)置測(cè)試系統(tǒng)

? ? ? ? ?首先準(zhǔn)備好一個(gè)內(nèi)核源代碼樹,構(gòu)造一個(gè)新內(nèi)核,然后安裝到自己的系統(tǒng)中。


? ? ? ? ? <>HelloWorld模塊

#include <linux/init.h> //定義了驅(qū)動(dòng)的初始化和退出相關(guān)的函數(shù) #include <linux/module.h> //定義了內(nèi)核模塊相關(guān)的函數(shù)、變量及宏MODULE_LICENSE("Dual BSD/GPL"); //該宏告訴內(nèi)核,該模塊採(cǎi)用自由許可證static int hello_init(void) //初始化函數(shù)僅在模塊載入時(shí)調(diào)用 {printk(KERN_ALERT"Helloworld\n");return 0; }static void hello_exit(void) //卸載函數(shù)僅在模塊卸載時(shí)調(diào)用 {printk(KERN_ALERT"Goodbye,cruelworld\n"); }module_init(hello_init); module_exit(hello_exit);

?? ?<>:核心模塊與應(yīng)用程序的對(duì)照

?? ? ? ?1.內(nèi)核模塊與應(yīng)用程序之間的種種不同:

? ? ? ?a.大多數(shù)小規(guī)模及中規(guī)模應(yīng)用程序是從頭到尾運(yùn)行單個(gè)任務(wù)。而模塊卻僅僅是預(yù)先注冊(cè)自己以便服務(wù)于將來的某個(gè)請(qǐng)求。然后它的初始化函數(shù)就馬上結(jié)束。

? ? ? ?b.事件驅(qū)動(dòng)的應(yīng)用程序和內(nèi)核代碼之間的還有一個(gè)不同是:應(yīng)用程序在退出時(shí)。能夠無論資源的釋放或者其它的清除工作,但模塊的退出函數(shù)卻必須細(xì)致撤銷初始化函數(shù)所做的一切,否則。在系統(tǒng)又一次引導(dǎo)之前某些東西就會(huì)殘留在系統(tǒng)中。

? ? ? ?c.應(yīng)用程序可以調(diào)用它并沒有定義的函數(shù),這是由于連接過程可以解析外部引用從而使用適當(dāng)?shù)暮?/span>數(shù)庫(kù)。而模塊只被鏈接到內(nèi)核,因此它能調(diào)用的函數(shù)不過內(nèi)核導(dǎo)出的那些函數(shù)。而不存在任何可鏈接的函數(shù)庫(kù)。

? ? ? ?d.內(nèi)核編程和用用編程的另外一點(diǎn)重要不同在于各環(huán)境下處理錯(cuò)誤的方式不同:應(yīng)用程序開發(fā)過程中的段錯(cuò)誤是無害的,而且總是能夠使用調(diào)試器跟蹤到源碼中的問題所在。而一個(gè)內(nèi)核錯(cuò)誤即使不影響整個(gè)系統(tǒng),也至少會(huì)殺死當(dāng)前進(jìn)程。

?

? ? ? ?2.用戶空間和內(nèi)核空間

? ? ? ?模塊運(yùn)行在所謂的內(nèi)核空間里,而應(yīng)用程序運(yùn)行在所謂的用戶空間里。

操作系統(tǒng)的作用是為應(yīng)用程序提供一個(gè)對(duì)計(jì)算機(jī)硬件的一致視圖。同一時(shí)候操作系統(tǒng)必須負(fù)責(zé)程序的獨(dú)立操作并保護(hù)資源不受非法訪問。這個(gè)任務(wù)僅僅有在CPU可以保護(hù)系統(tǒng)軟件不受應(yīng)用程序破換時(shí)才干完畢。人們選擇的方法是在CPU中實(shí)現(xiàn)不同操作模式(或者級(jí)別)。每當(dāng)應(yīng)用程序運(yùn)行系統(tǒng)調(diào)用或者被硬件中斷掛起時(shí),Unix將運(yùn)行模式從用戶空間切換到內(nèi)核空間。

模塊化代碼在內(nèi)核空間中運(yùn)行。用于擴(kuò)展內(nèi)核的功能。

通常一個(gè)驅(qū)動(dòng)程序要運(yùn)行兩個(gè)任務(wù):模塊中的某些函數(shù)作為系統(tǒng)調(diào)用的一部分而運(yùn)行。而其它函數(shù)則負(fù)責(zé)中斷處理。

?

? ? ? ? 3.內(nèi)核中的并發(fā)

? ? ? ?有幾方面的原因促使內(nèi)核編程必須考慮并發(fā)問題:

? ? ? ?a.Linux系統(tǒng)中通常正執(zhí)行多個(gè)并發(fā)進(jìn)程。而且可能有多個(gè)進(jìn)程同一時(shí)候使用我們的驅(qū)動(dòng)程序。

? ? ? ?b.大多數(shù)設(shè)備可以中斷處理器。而中斷處理程序異步執(zhí)行,并且可能在驅(qū)動(dòng)程序正試圖處理其它任務(wù)時(shí)被調(diào)用

? ? ? ?c.有一些軟件抽象也在異步執(zhí)行著。

? ? ? ?d.Linux還能夠執(zhí)行在對(duì)稱多處理器系統(tǒng)上。因此可能同一時(shí)候有不止一個(gè)CPU執(zhí)行我們的驅(qū)動(dòng)程序。

? ? ? ?e.2.6中內(nèi)核代碼已經(jīng)是搶占式的,這意味著即使在單處理器系統(tǒng)上也存在很多類似多處理器系統(tǒng)的并發(fā)問題。

?

? ? ? ?4.當(dāng)前進(jìn)程

? ? ? ?內(nèi)核代碼可通過訪問全局項(xiàng)current來獲得當(dāng)前進(jìn)程。

current<asm/current.h>中定義。是一個(gè)指向struct task_struct的指針,而task_struct結(jié)構(gòu)在<linux/sched.h>文件里定義。current指針指向當(dāng)前正執(zhí)行的進(jìn)程。在open/read等系統(tǒng)調(diào)用的執(zhí)行過程中。當(dāng)前進(jìn)程指的是調(diào)用這些系統(tǒng)調(diào)用的進(jìn)程。

? ? ? ?假設(shè)須要。內(nèi)核代碼能夠通過current獲得與當(dāng)前進(jìn)程相關(guān)的信息。

? ? ? ?設(shè)備驅(qū)動(dòng)程序僅僅要包括<linux/sched.h>頭文件就可以引用當(dāng)前進(jìn)程。比如,以下的語句通過訪問struct task_struct

的某些成員來打印當(dāng)前進(jìn)程的ID和命令名:

printk(KERN_INFO"The process is\"%s\"(pid%i)\n,current->comm,current->pid);

?

? ? ? ?5.其它一些細(xì)節(jié)

? ? ? ?應(yīng)用程序在虛擬內(nèi)存中布局,并具有一塊非常大的棧空間。當(dāng)然,棧是用來保存函數(shù)調(diào)用歷史以及當(dāng)前活動(dòng)函數(shù)中的自己主動(dòng)變量的。而相反的是,內(nèi)核具有非常小的棧,它可能僅僅和一個(gè)4096字節(jié)大小的頁那樣小。

? ? ? ?常常會(huì)在內(nèi)核API中看到具有兩個(gè)下劃線前綴(__)的函數(shù)名稱。

具有這樣的名稱的函數(shù)一般是接口的底層組件。應(yīng)該慎重使用。


? ? ? ?<>:編譯和裝載

? ? ? ?1.編譯模塊

? ? ? ? 首先來看看模塊時(shí)怎樣構(gòu)造的,詳細(xì)細(xì)節(jié)參考內(nèi)核源碼中Documentation/kbuild文件夾下的文件。

在構(gòu)造內(nèi)核模塊前。應(yīng)確保具有正確版本號(hào)的編譯器、模塊工具盒其它必要的工具。

內(nèi)核文檔文件夾中Documentation/Changes文件列出了須要的工具版本號(hào)。makefile里的一些規(guī)則:

? ? ? ? ?假設(shè)要構(gòu)造的模塊名稱為module.ko。并由兩個(gè)源文件生成(比方file1.cfile2.c),則正確的makefile可例如以下編寫:

obj-m := module.o module-objs := file1.o file2.o

? ? ? ?2.裝載和卸載模塊

? ? ? ?裝載模塊一般使用insmod程序,它和ld有些類似,它將模塊的代碼和數(shù)據(jù)裝入內(nèi)核,然后使用內(nèi)核的符號(hào)表解析模塊中不論什么為解析的符號(hào)。

然而,與鏈接器不同,內(nèi)核不會(huì)改動(dòng)模塊的磁盤文件,而只改動(dòng)內(nèi)存中的副本。insmod能夠接受一些命令行選項(xiàng),而且能夠在模塊鏈接到內(nèi)核之前給模塊中的整型和字符串型變量賦值。

insmod類似的是modprobe工具,它不僅裝載該模塊。還裝載該模塊所一欄的模塊。

? ? ? ? 卸載模塊用rmmod工具。可從內(nèi)核中移除模塊。假設(shè)內(nèi)核覺得模塊仍然在使用狀態(tài)。或者內(nèi)核被配置為禁止移除模塊,則無法移除該模塊。

? ? ? ? lsmod程序列出當(dāng)前裝載到內(nèi)核中的全部模塊,還提供了其它一些信息,比方其它模塊是不是在使用某個(gè)特定模塊等。

? ? ? 3.版本號(hào)依賴

? ? ? 4.平臺(tái)依賴

?

? ? ? <>:內(nèi)核符號(hào)表

? ? ? ?insmod使用公共內(nèi)核符號(hào)表來解析模塊中沒有定義的符號(hào)。公共內(nèi)核符號(hào)表中包括了全部全局內(nèi)核項(xiàng)的地址,這是實(shí)現(xiàn)模塊驅(qū)動(dòng)程序所必須的。當(dāng)模塊被裝入內(nèi)核后。它所導(dǎo)出的不論什么符號(hào)都會(huì)變成內(nèi)核符號(hào)表的一部分。

新模塊能夠使用由我們自己的模塊導(dǎo)出的符號(hào),這樣。我們能夠在其它模塊上層疊新的模塊。

? ? ? ?modprobe是處理層疊模塊的一個(gè)使用工具。它的功能在非常大程度上和insmod類似,可是它除了裝入指定模塊外還同一時(shí)候裝入指定模塊所依賴的其它模塊。通過層疊技術(shù)。我們能夠?qū)⒛K劃分為多個(gè)層,通過簡(jiǎn)化每一個(gè)層可縮短開發(fā)時(shí)間。

? ? ? ?Linux內(nèi)核頭文件提供了一個(gè)方便的方法來管理符號(hào)對(duì)模塊外部的可見性。從而降低了可能造成的名字空間污染。而且適當(dāng)隱藏信息。假設(shè)一個(gè)模塊須要向其它模塊導(dǎo)出符號(hào),則應(yīng)該使用以下的宏。

EXPORT_SYMBOL(name)

EXPORT_SYMBOL_GPL(name)

? ? ? ?GPL版本號(hào)使得要導(dǎo)出的模塊僅僅能僅僅能被GPL許可證下的模塊使用。

符號(hào)必須在模塊文件的全局部分導(dǎo)出。不能在函數(shù)中導(dǎo)出,這是由于上面這兩個(gè)宏將被擴(kuò)展為一個(gè)特殊變量的聲明,而該變量必須是全局的。該變量將在模塊可運(yùn)行文件的特殊部分(即一個(gè)“ELF段”)中保存,在裝載時(shí),內(nèi)核通過這個(gè)段來尋找模塊導(dǎo)出的變量。

?

? ? ? ?<>:預(yù)備知識(shí)

? ? ? ?大部分內(nèi)核代碼中都要包括相當(dāng)數(shù)量的頭文件,以便獲得函數(shù)、數(shù)據(jù)類型和變量的定義。有幾個(gè)頭文件時(shí)專門用于模塊的,因此必須在出如今每一個(gè)可裝載的模塊中。

因此。全部的模塊代碼中都包括以下兩行代碼:

#include <linux/module.h>? /*module.h包括有可裝載模塊須要的大量符號(hào)和函數(shù)的定義*/

#include <linux/init.h>??? /*包括init.h的目的是指定初始化和清除函數(shù)*/

大部分模塊還包含moduleparam.h頭文件,這樣就能夠在裝載模塊時(shí)向模塊傳遞參數(shù)。

MODULE_LICENSE("GPL");內(nèi)核可以識(shí)別的該許可證。

假設(shè)一個(gè)模塊沒有顯示地標(biāo)記為上述內(nèi)核可識(shí)別的許可證,則會(huì)假定是專有的。而內(nèi)核裝載這樣的模塊就會(huì)被“污染”。

? ? ? ?可在模塊中包括的其它描寫敘述性定義為包括MODULE_AUTHOR(描寫敘述模塊作者)MODULE_DESCRIPTION(用來說明模塊用途的簡(jiǎn)短描寫敘述)MODULE_VERSION(代碼修訂號(hào))等。

?

? ? ? ? ?<>:初始化和關(guān)閉

?? ? ? 1.模塊的初始化函數(shù)負(fù)責(zé)注冊(cè)模塊所提供的不論什么設(shè)施。

這里的設(shè)施指的是一個(gè)能夠被應(yīng)用程序訪問的新功能,它可能是一個(gè)完整的驅(qū)動(dòng)程序或者不過一個(gè)新的軟件抽象。初始化函數(shù)的實(shí)際定義通常例如以下所看到的:

static int __init initialization_function(void)

{

??? /*這里時(shí)初始化代碼*/

}

module_init(initialization_function);

初始化函數(shù)應(yīng)該被聲明為static,由于這樣的函數(shù)在特定文件之外沒有其它意義。__init標(biāo)記對(duì)內(nèi)核來講是一種暗示,表明該函數(shù)僅在初始化期間使用。

在模塊被裝載之后,模塊裝載器就會(huì)將初始化函數(shù)扔掉,這樣可將該函數(shù)占用的內(nèi)存釋放出來。以作他用注意,不要在結(jié)束初始化之后仍要使用的函數(shù)上使用這兩個(gè)標(biāo)記。

module_initd的使用是強(qiáng)制的。這個(gè)宏在模塊的目標(biāo)代碼中添加一個(gè)特殊的段。用于說明內(nèi)核初始化函數(shù)所在的位置。沒有這個(gè)定義,初始化函數(shù)永遠(yuǎn)不會(huì)被調(diào)用。模塊能夠注冊(cè)很多不同類型的設(shè)施。包含不同類型的設(shè)備、文件系統(tǒng)、password交換等。對(duì)于每種設(shè)施,相應(yīng)有詳細(xì)的內(nèi)核函數(shù)用來完畢注冊(cè)。大部分注冊(cè)函數(shù)名字帶有register_前綴。

?

? ? ? ?2.清除函數(shù)

? ? ? ?每一個(gè)重要的模塊都須要一個(gè)清除函數(shù),該函數(shù)在模塊被移除前注銷接口并向系統(tǒng)中返回全部資源。該函數(shù)定義例如以下:

static void __exit cleanup_function(void)

{

??? /*這里是清除代碼*/

}

module_exit(cleanup_function)

__exit修飾詞標(biāo)記該代碼僅用于模塊卸載(編譯器將把該函數(shù)放在特殊的ELF段中)。假設(shè)模塊被直接內(nèi)嵌到內(nèi)核中,或者內(nèi)核的配置不同意卸載模塊,則被標(biāo)記為__exit的函數(shù)將被簡(jiǎn)單地丟棄。

出于以上原因,被標(biāo)記為__exit的函數(shù)僅僅能在模塊被卸載或者系統(tǒng)關(guān)閉時(shí)被調(diào)用。其它的不論什么使用方法都是錯(cuò)誤的。module_exit聲明為對(duì)于幫助內(nèi)核找到模塊的清除函數(shù)式必須的。

假設(shè)一個(gè)模塊沒有定義清除函數(shù)。則內(nèi)核不同意卸載該模塊。

?

? ? ? ? 3.初始化過程中的錯(cuò)誤處理

?

? ? ? ? 4.模塊裝載競(jìng)爭(zhēng)

? ? ? ?a.在注冊(cè)完畢之后,內(nèi)核的某些部分可能會(huì)馬上使用我們剛剛注冊(cè)的不論什么設(shè)施。即。在初始化函數(shù)還在執(zhí)行的時(shí)候。內(nèi)核就全然可能會(huì)調(diào)用我們的模塊。因此,在首次注冊(cè)完畢之后。代碼就應(yīng)該準(zhǔn)備好被內(nèi)核的其它部分調(diào)用;在用來支持某個(gè)設(shè)施的全部?jī)?nèi)部初始化完畢之前。不要注冊(cè)不論什么設(shè)施。

? ? ? ?b.當(dāng)初始化失敗而內(nèi)核的某些部分已經(jīng)使用了模塊所注冊(cè)的某個(gè)設(shè)施時(shí)應(yīng)該怎樣處理。

假設(shè)這樣的情況可能發(fā)生在我們的模塊上,則根本不應(yīng)該出現(xiàn)初始化失敗的情況,畢竟模塊已經(jīng)成功導(dǎo)出了可用的功能及符號(hào)。假設(shè)初始化一定要失敗。則應(yīng)該細(xì)致處理內(nèi)核其它部分正在進(jìn)行的操作,而且要等待這些操作的完畢。

?

? ? ? ?<>:模塊參數(shù)

? ? ? 因?yàn)橄到y(tǒng)的不同,驅(qū)動(dòng)程序須要的參數(shù)或許會(huì)發(fā)生變化。

這包含設(shè)備編號(hào)以及其它一些用來控制驅(qū)動(dòng)程序操作方式的參數(shù)。內(nèi)核同意對(duì)驅(qū)動(dòng)程序指定參數(shù),而這些參數(shù)可在裝載驅(qū)動(dòng)程序模塊時(shí)改變。

這些參數(shù)的值可在執(zhí)行insmodmodprobe命令裝載模塊時(shí)賦值,而modprobe還能夠從它的配置文件(/etc/modprob.conf)中讀取參數(shù)值。

? ? ? 在insmod改變模塊參數(shù)之前,模塊必須讓這些參數(shù)對(duì)insmod命令可見。

參數(shù)必須使用module_param宏來聲明,這個(gè)宏在moduleparam.h中定義。module_param須要三個(gè)參數(shù):變量的名稱、類型以及用于sysfs入口項(xiàng)的訪問許可掩碼。這個(gè)宏必須放在不論什么函數(shù)之外,一般是在源文件的頭部。比如:

static char *whom = "world";

static int howmany = 1;

module_param(howmany, int, S_IRUGO);

module_param(whom, charp, S_IRUGO);

內(nèi)核支持的模塊參數(shù)類型有:?bool invbool charp int long short uint ulongushort

模塊裝載器也支持?jǐn)?shù)組參數(shù),在提供數(shù)組值時(shí)用逗號(hào)劃分個(gè)數(shù)組成員。要聲明數(shù)組參數(shù),須要使用以下的宏:

module_param_arry(name, type, num, perm);

當(dāng)中name是數(shù)組名稱。type是數(shù)組元素類型,num是一個(gè)整型變量。而perm它是一種常見的訪問許可證值。



版權(quán)聲明:本文博主原創(chuàng)文章,博客,未經(jīng)同意不得轉(zhuǎn)載。

總結(jié)

以上是生活随笔為你收集整理的LINUX设备驱动程序的注意事项(两)建设和执行模块的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。