www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > > 嵌入式云IOT技術(shù)圈
[導(dǎo)讀]開篇,我們先來了解下什么是用戶態(tài)、內(nèi)核態(tài)。

本人才疏學(xué)淺,若有說得不對的地方,愿各位大佬指教!

微信公眾號:morixinguan
關(guān)注可了解更多的教程。問題或建議,請公眾號留言;
[如果你覺得本文對你有幫助,歡迎贊賞]

▲長按圖片保存可分享至朋友圈



一、用戶態(tài)、內(nèi)核態(tài)

開篇,我們先來了解下什么是用戶態(tài)、內(nèi)核態(tài)。

一般現(xiàn)代CPU都有幾種不同的指令執(zhí)行級別。

在高執(zhí)行級別下,代碼可以執(zhí)行特權(quán)指令,訪問任意的物理地址,這種CPU執(zhí)行級別就對應(yīng)著內(nèi)核態(tài)。

而在相應(yīng)的低級別執(zhí)行狀態(tài)下,代碼的掌控范圍會受到限制。只能在對應(yīng)級別允許的范圍內(nèi)活動。

舉例:

intel x86 CPU有四種不同的執(zhí)行級別0-3,linux只使用了其中的0級和3級分別來表示內(nèi)核態(tài)和用戶態(tài)。

二、如何區(qū)分用戶態(tài)和內(nèi)核態(tài)?

一般來說在linux中,地址空間是一個顯著的標(biāo)志:0xc0000000以上的地址空間只能在內(nèi)核態(tài)下訪問,0x00000000-0xbfffffff的地址空間在兩種狀態(tài)下都可以訪問。

注意:這里所說的地址空間是邏輯地址而不是物理地址。

孟寧老師在講解內(nèi)核知識點已經(jīng)把這個知識點最精華的部分提取出來了,那么到底內(nèi)核中有什么樣的接口是跟老師說的相關(guān)的呢?

其實寫過linux內(nèi)核驅(qū)動程序的同學(xué)應(yīng)該就知道,實現(xiàn)一個字符設(shè)備驅(qū)動,在write方法和read方法中,內(nèi)核態(tài)和用戶態(tài)之間的橋梁就是copy_to_user和copy_form_user這兩個接口,因為在高執(zhí)行級別下,代碼可以執(zhí)行特權(quán)指令,訪問任意的物理地址,這種CPU執(zhí)行級別就對應(yīng)著內(nèi)核態(tài),而在相應(yīng)的低級別執(zhí)行狀態(tài)下,代碼的掌控范圍會受到限制。只能在對應(yīng)級別允許的范圍內(nèi)活動。其實正好說明了這個問題,程序員或者軟件應(yīng)用工程師在編寫應(yīng)用程序去控制設(shè)備驅(qū)動的時候,首先肯定是會打開相應(yīng)的文件描述符,然后對相應(yīng)的文件描述符進行讀寫,ioctl,lseek之類的操作。當(dāng)在應(yīng)用層編寫程序即是屬于用戶態(tài),在應(yīng)用程序不能訪問任意的硬件物理地址,所以當(dāng)用戶需要讀取文件描述符的內(nèi)的內(nèi)容時,就需要調(diào)用read,當(dāng)用戶需要寫數(shù)據(jù)進文件描述符時,就需要用write,在用戶態(tài)調(diào)用這兩個接口,進而就會進行系統(tǒng)調(diào)用,產(chǎn)生相應(yīng)的系統(tǒng)調(diào)用號,然后內(nèi)核會根據(jù)系統(tǒng)調(diào)用號找到相應(yīng)的驅(qū)動程序,此時系統(tǒng)就處在內(nèi)核態(tài)中,在驅(qū)動程序中,首先進行驅(qū)動程序初始化,然后注冊,產(chǎn)生驅(qū)動程序最重要主設(shè)備號和次設(shè)備號。初始化的過程中由于對相應(yīng)的read方法和wirte方法進行初始化,故用戶態(tài)執(zhí)行read操作,就會進而使CPU產(chǎn)生異常,然后進入內(nèi)核態(tài)找到相應(yīng)的read,write當(dāng)然也是一樣的。

當(dāng)然,我的分析依然還是處于非常片面的,只能說個大概,因為操作系統(tǒng)在執(zhí)行系統(tǒng)調(diào)用的過程依然是非常復(fù)雜的,但是復(fù)雜歸復(fù)雜,對于這樣的一個流程我們還是應(yīng)當(dāng)要去了解清楚。

還有一個例子就是,假設(shè)我需要實現(xiàn)一個led驅(qū)動或者其它的驅(qū)動,在內(nèi)核驅(qū)動中,我需要將相應(yīng)的物理地址ioremap成為一個虛擬地址,當(dāng)驅(qū)動調(diào)用結(jié)束后,還應(yīng)當(dāng)取消相應(yīng)的地址映射,這其實就是在內(nèi)核態(tài)進行的操作,因為在內(nèi)核中,訪問這些地址以虛擬地址的形式進行相應(yīng)的內(nèi)存分配。為了使軟件訪問I/O內(nèi)存,必須為設(shè)備分配虛擬地址.這就是ioremap的工作。

以上是Linux內(nèi)核中斷以及異常需要了解的基礎(chǔ)。

三、中斷基礎(chǔ)

中斷,通常被定義為一個事件。打個比方,你燒熱水,水沸騰了,這時候你要去關(guān)掉燒熱水的電磁爐,然后再去辦之前手中停不下來的事情。那么熱水沸騰就是打斷你正常工作的一個信號機制。當(dāng)然,還有其它的情況,我們以后再做分析。

中斷也就是這樣產(chǎn)生的,中斷分為同步中斷還有異步中斷。

同步中斷在Intel的手冊中被稱為異常,而異步中斷被稱作中斷。打個比方在ARM處理器的異常種類就有不少,有未定義指令異常,軟中斷異常,快中斷異常等等。異常是由程序錯誤產(chǎn)生的,或者是內(nèi)核必須處理的異常條件產(chǎn)生的。如果你曾經(jīng)學(xué)過單片機,那么你一定會清楚,51單片機的P32,P33是外部中斷0和1,假設(shè)當(dāng)你在程序中開啟了外部中斷0,然后在中斷中執(zhí)行了相應(yīng)的程序,這時你在外部中斷0的一腳連接一個按鍵,這時候你按下去P30這個引腳就會產(chǎn)生一個中斷。那么中斷服務(wù)程序就會響應(yīng)你的操作,比如點亮一個LED燈,或者說讓蜂鳴器叫一下。

那么在linux內(nèi)核中的中斷其實也是和單片機類似的,只不過linux內(nèi)核的中斷定義的比較豐富,但是基本思想還是一樣的。linux內(nèi)核處理中斷有一種叫做中斷信號的機制。它的作用就是當(dāng)一個中斷信號到來時,CPU必須停止它當(dāng)然正在做的事情,然后切換到一個新的活動,為了做到這一點,內(nèi)核態(tài)堆棧保存的程序計數(shù)器的當(dāng)前值,其實就是eip和cs寄存器的存儲數(shù)據(jù),然后把中斷相關(guān)類型的一個地址放到一個程序計數(shù)器當(dāng)中去。

其實在內(nèi)核中,中斷這樣的切換機制很像進程的調(diào)度,上下文切換這樣的機制,但是依然存在著一個非常明顯的差異,那就是中斷或者異常在處理的代碼并不是一個進程。

中斷信號的來臨必將會引起中斷的處理,那么中斷處理必須要滿足以下的約束:

1、linux內(nèi)核在響應(yīng)中斷以后必須要進行的操作分為兩部分:我們把非常重要的,非常緊急的處理程序讓內(nèi)核立即去運行。剩下的有延時的部分就讓它后面再去執(zhí)行。這樣也就驗證了水沸騰,而人停下手中的事去關(guān)電磁爐,再回去做他的事一樣的道理。

2、中斷編寫的程序必須編寫成可以使內(nèi)核控制的路徑能以嵌套的方式來執(zhí)行,或者說,當(dāng)最后一個內(nèi)核控制路徑終止的時候,內(nèi)核必須能恢復(fù)被中斷進程的執(zhí)行,或者說,中斷信號已經(jīng)導(dǎo)致了進程重新調(diào)度,內(nèi)核能切換到另外一個進程。這是我們分析的另外一種情況,水開了,人去關(guān)電磁爐,然后人接著做事,這是第一種情況。水開了,人去關(guān)電磁爐,接下來門鈴響了,客人來了,你必須去迎接客人,然后就打斷了你之前在做的事情,也就是客人來了打斷了你正在做的這件事進入到與陪客的階段。

3、在臨界區(qū)中,中斷必須要被禁止。臨界區(qū)其實就是加鎖和去鎖的實現(xiàn)。程序員將非常關(guān)鍵的步驟放進臨界區(qū),就是為了防止中斷或者其它的信號去影響到它,其實在內(nèi)核中這樣的步驟是有必要的。臨界區(qū)的代碼必須在短時間內(nèi)被執(zhí)行,而不應(yīng)該出現(xiàn)延時的操作,且必須盡可能的去限制這樣的臨界區(qū),因為,內(nèi)核在處理中斷程序的時候,應(yīng)該是在大部分時間以開中斷的方式去運行。

在intel的文檔中,中斷和異常通常分為幾類:中斷有可屏蔽的,不可屏蔽的。異常有處理器探測異常,這就包括故障的產(chǎn)生,陷阱,異常的中止,還有編程異常的狀況。每個中斷和異常都是由0-255之間的一個數(shù)來標(biāo)識。intel管這東西叫向量。

其實在ARM中就有那么一張表叫異常向量表,那就是我剛剛文章里說過的那幾個。

在linux中也有這么一張表:

在linux內(nèi)核中,每一個能夠發(fā)出中斷請求的硬件設(shè)備控制器都有一條名為IRQ的輸出線。所有現(xiàn)在存在的IRQ線都與一個名為可編程中斷控制器的硬件電路的輸入引腳相連,上次講到單片機的時候,我就講到了單片機中斷的一些概念。我們現(xiàn)在來看一幅圖,更好說明一個問題:

下面的這幅圖是51單片機的一個關(guān)于矩陣鍵盤的學(xué)習(xí)的一個proteus的仿真電路圖。

其中P3.2和P3.3為外部中斷引腳,當(dāng)可編程控制器(51MCU)收到外部中斷響應(yīng)的時候,會執(zhí)行一些特定的操作,當(dāng)然這需要開發(fā)者去編寫一個中斷初始化程序和一個中斷服務(wù)程序。

那么,可編程中斷控制器會做以下的操作:

1、監(jiān)視IRQ線,我們可以理解就是監(jiān)視單片機外部中斷的IO口,檢查產(chǎn)生的信號。如果有條或者兩條以上的IRQ上產(chǎn)生信號,就選擇引腳編號較小的IRQ線。

2、如果一個引發(fā)信號出現(xiàn)在IRQ線上:

a.把接收到的引發(fā)信號轉(zhuǎn)換成對應(yīng)的向量。

b.把這個向量存放在中斷控制器的一個I/O端口,從而允許CPU通過數(shù)據(jù)總線讀這個向量。

c.把引發(fā)信號發(fā)送到處理器的INTR引腳,即會產(chǎn)生一個中斷。

d.等待,直到CPU通過把這個中斷信號寫進可編程中斷控制器的一個I/O端口來確認它,當(dāng)這種情況發(fā)送時,清INTR線。

3、最后一步返回到第一步繼續(xù)監(jiān)視,然后依次執(zhí)行。

當(dāng)然,也存在著一些更加高級的可編程中斷控制器,其中ARM算一種,Intel也是,等等。。。單片機算是最簡單的一種。像多APIC系統(tǒng)的結(jié)構(gòu),會存在以下的一個圖的關(guān)系:

中斷信號通過IO引腳,然后通過中斷控制器I2C總線與相應(yīng)的CPU進行通信。

一般情況下,有兩種分發(fā)的方式:

1、靜態(tài)分發(fā)模式:IRQ信號傳遞給重定向表相應(yīng)的項中所列出的本地APIC,然后中斷立即傳遞一個特定的CPU,或者是一組CPU,或者是所有的CPU。其實這是廣播模式的一種模型,接觸過UNIX網(wǎng)絡(luò)編程應(yīng)該會知道。

2、動態(tài)分發(fā)模式:如果處理器正在執(zhí)行最低優(yōu)先級的進程,IRQ信號線就會傳遞給這種處理器的本地APIC。也就是說,在CPU內(nèi)部有一個控制優(yōu)先級的寄存器,用來計算當(dāng)前運行進程的優(yōu)先級。如果兩個或者多個CPU共享最低優(yōu)先級,那么就利用仲裁的技術(shù)在這些CPU之間分配負荷等等的形式。

異常有很多種,在8086處理器可以找到多達20種不同的異常,內(nèi)核必須為每種異常提供一個專門的異常處理程序。對于某些異常,CPU控制單元會在開始執(zhí)行異常處理程序前產(chǎn)生一個硬件出錯碼,并且壓入內(nèi)核態(tài)的堆棧中去。

關(guān)于這個異常處理信息,我們有必要來了解以下perror這個函數(shù)。

perror( ) 用來將上一個函數(shù)發(fā)生錯誤的原因輸出到標(biāo)準(zhǔn)設(shè)備(stderr)。參數(shù) s 所指的字符串會先打印出,后面再加上錯誤原因字符串。此錯誤原因依照全局變量errno(這里的說法不準(zhǔn)確,errno是一個宏,該宏返回左值) 的值來決定要輸出的字符串。在庫函數(shù)中有個errno變量,每個errno值對應(yīng)著以字符串表示的錯誤類型。當(dāng)你調(diào)用"某些"函數(shù)出錯時,該函數(shù)已經(jīng)重新設(shè)置了errno的值。perror函數(shù)只是將你輸入的一些信息和現(xiàn)在的errno所對應(yīng)的錯誤一起輸出。

用法:void perror(const char *s); perror ("open_port");

我們寫段代碼來看看就知道了:

 1#include   2#include   3  4int main(void)  5{  6 FILE?*filp?= NULL;  7 filp?=?fopen("txt","r");  8 if(NULL ==?filp)  9 { 10 perror("沒有相應(yīng)的文件"); 11 } 12 fclose(filp); 13 return 0 ; 14}

運行結(jié)果:

在當(dāng)前目錄下,找不到txt這個文件,所以perror會根據(jù)相應(yīng)的出錯信息打印No such file or directory。

看了這個函數(shù)的應(yīng)用,相信更會理解上面的異常的相關(guān)知識。當(dāng)然還有更多日常寫程序發(fā)現(xiàn)的BUG,比如段錯誤,段錯誤是最常見的,一些初學(xué)者在使用指針的時候,沒有分配相應(yīng)的空間,這時候給指針賦值,雖然沒有語法錯誤,但可能會有警告。當(dāng)程序運行的時候,就會自動退出并提示段錯誤(Segment fault),這一般是在linux上會出現(xiàn)這兩個英語單詞,在window的Devcpp上是這樣,:

段錯誤的產(chǎn)生原因有很多種,程序在進行遞歸的時候,如果沒有相應(yīng)的條件退出的話,程序一旦進行死循環(huán)遞歸之后就會產(chǎn)生爆棧錯誤,也就是棧被擠爆了,棧這個概念其實并不陌生。我們在寫C語言程序的時候,一旦寫了一個子函數(shù),那就相當(dāng)于建立了一個堆棧,一般情況下函數(shù)在執(zhí)行完退出后堆棧是自動分配,自動銷毀的,不用程序員去手動malloc申請內(nèi)存再free釋放內(nèi)存。因為手動分配的內(nèi)存是用了堆區(qū)的內(nèi)存,而自動分配是在棧區(qū)進行分配的。在32位操作系統(tǒng)上,棧的大小就只有12M,所以寫代碼的時候,一定要記得防止爆棧錯誤的產(chǎn)生,特別是遞歸!在main函數(shù)中多寫些子函數(shù)是有好處的,要養(yǎng)成良好的編程習(xí)慣。

四、Linux中斷應(yīng)用

接下來我們結(jié)合一個真實的驅(qū)動案例來描述linux內(nèi)核中驅(qū)動的中斷機制,首先我們先了解一下linux內(nèi)核中提供的中斷接口。

這個接口我們需要包含一個頭文件:#include ,在Linux中斷編程中,最重要的是要了解以下的接口函數(shù):

1、這個是請求中斷函數(shù)

 1int request_irq(unsigned?int?irq,?irq_handler_t?handler,  2 unsigned?long?irqflags,?const?char?*devname,?void?*dev_id)  3 irq:  4 中斷號?arch/arm/plat-s3c64xx/include/plat/irqs.h  5 handler:  6 中斷處理函數(shù)?irqreturn_t?handler(int irq,?void?*dev_id);  7 irqreturn_t:  8 See?include/linux/irqreturn.h  9 10 irqflags: 11 See?line 21-59 in?include/linux/interrupt.h 12 使用IRQF_SHARED共享irq時,?irqflags必須相同 13 如:??????request_irq(IRQ_EINT(0),?handler1, 14 IRQF_TRIGGER_FALLING?|?IRQF_SHARED, "dev1",?&dev1); 15 16 request_irq(IRQ_EINT(0),?handler2, 17 IRQF_TRIGGER_FALLING?|?IRQF_SHARED, "dev2",?&dev2); 18 19 devname: 20 設(shè)備名,?cat?/proc/interrupts 21 22 dev_id: 23 發(fā)生中斷時將dev_id傳遞給handler函數(shù), 24 irqflags含有IRQF_SHARED時dev_id不能為NULL,?并且要保證唯一 25 dev_id一般采用當(dāng)前設(shè)備的結(jié)構(gòu)體指針

2、釋放中斷

1void free_irq ( unsigned int irq, void *?dev_id); 2 釋放匹配irq和dev_id的中斷,?如果irq有多個相同的dev_id,?將釋放第一個 3 So,?共享中斷的dev_id不是唯一時,?可能會釋放到其它設(shè)備的中斷

3、開啟中斷

1void?enable_irq(unsigned?int?irq); 2 開啟irq號中斷

4、關(guān)閉中斷

1void?disable_irq(unsigned?int?irq); 2 關(guān)閉irq號中斷

5、關(guān)閉當(dāng)前CPU中斷并保存在flag中去

1void local_irq_save(unsigned long flags);

6、恢復(fù)flag到CPU中去

1void local_irq_restore(unsigned long flags); 2 恢復(fù)flags到當(dāng)前CPU

7、關(guān)閉當(dāng)前的CPU中斷

1void local_irq_disable(void);

8、開始當(dāng)前的CPU中斷

1void local_irq_enable(void);

接下來我們來看一個按鍵中斷的例子,這個例子是基于Tiny4412按鍵驅(qū)動的源碼:

使用的Linux3.5的內(nèi)核,雖然版本有點老,但作為開發(fā)者來說,其實并沒有什么差別。

 1#include   2#include   3#include   4#include   5#include   6#include   7#include   8#include   9#include   10#include   11#include   12#include   13#include   14#include   15#include   16#include   17#include   18  19#include   20#include   21#include   22#include   23  24//設(shè)備名稱  25#define DEVICE_NAME "buttons"  26  27struct button_desc {  28 int gpio;  29 int number;  30 char *name;  31 struct timer_list timer;  32};  33  34//定義按鍵相關(guān)的寄存器  35static struct button_desc buttons[]?=?{  36 {?EXYNOS4_GPX3(2), 0, "KEY0" },  37 {?EXYNOS4_GPX3(3), 1, "KEY1" },  38 {?EXYNOS4_GPX3(4), 2, "KEY2" },  39 {?EXYNOS4_GPX3(5), 3, "KEY3" },  40};  41  42//存儲按鍵的鍵值  43static volatile char key_values[]?=?{  44 '0', '0', '0', '0', '0', '0', '0', '0'  45};  46  47//創(chuàng)建一個等待隊列頭并初始化  48static DECLARE_WAIT_QUEUE_HEAD(button_waitq);  49  50static volatile int ev_press?= 0;  51  52//按鍵定時器  53static void tiny4412_buttons_timer(unsigned long _data)  54{  55 struct button_desc *bdata =?(struct button_desc *)_data;  56 int down;  57 int number;  58 unsigned tmp;  59 //獲取按鍵的值  60 tmp?=?gpio_get_value(bdata->gpio);  61 //判斷是否為低電平  62 down?=?!tmp;  63 printk(KERN_DEBUG "KEY?%d:?%08x\n",?bdata->number,?down);  64  65 number?=?bdata->number;  66 //如果此時不為低電平,中斷處理進入休眠狀態(tài),一般有事件產(chǎn)生就會立即被喚醒  67 if (down?!=?(key_values[number]?& 1))?{  68 key_values[number]?= '0' +?down;  69  70 ev_press?= 1;  71 //中斷休眠  72 wake_up_interruptible(&button_waitq);  73 }  74}  75//按鍵中斷處理函數(shù)  76//irq:中斷號  77//dev_id:設(shè)備ID號  78static irqreturn_t button_interrupt(int irq, void *dev_id)  79{  80 struct button_desc *bdata =?(struct button_desc *)dev_id;  81 //注冊一個定時器  82 mod_timer(&bdata->timer,?jiffies?+?msecs_to_jiffies(40));  83 //返回一個中斷句柄  84 return IRQ_HANDLED;  85}  86//按鍵打開函數(shù)  87//inode?:?節(jié)點  88//file?:?打開文件的形式  89static int tiny4412_buttons_open(struct?inode?*inode,?struct?file?*file)  90{  91 int irq;  92 int i;  93 int err?= 0;  94 //循環(huán)遍歷四個IO口,看看有哪個按鍵被按下了  95 for (i?= 0;?i?< ARRAY_SIZE(buttons); i++) {  96 if (!buttons[i].gpio)  97 continue;  98 //初始化定時器  99 setup_timer(&buttons[i].timer,?tiny4412_buttons_timer, 100 (unsigned long)&buttons[i]); 101 //設(shè)置GPIO為中斷引腳,也就是對應(yīng)那四個按鍵 102 irq?=?gpio_to_irq(buttons[i].gpio); 103 err?=?request_irq(irq,?button_interrupt,?IRQ_TYPE_EDGE_BOTH, //請求中斷處理函數(shù) 104 buttons[i].name,?(void *)&buttons[i]); 105 if (err) 106 break; 107 } 108 109 if (err)?{ 110 i--; 111 for (;?i?>= 0;?i--)?{ 112 if (!buttons[i].gpio) 113 continue; 114 115 irq?=?gpio_to_irq(buttons[i].gpio); 116 disable_irq(irq); //關(guān)中斷 117 free_irq(irq,?(void *)&buttons[i]);//釋放中斷 118 119 del_timer_sync(&buttons[i].timer);//刪除一個定時器 120 } 121 122 return -EBUSY; 123 } 124 125 ev_press?= 1; 126 return 0; 127} 128//按鍵關(guān)閉處理函數(shù) 129static int tiny4412_buttons_close(struct?inode?*inode,?struct?file?*file) 130{ 131 int irq,?i; 132 133 for (i?= 0;?i?< ARRAY_SIZE(buttons); i++) { 134 if (!buttons[i].gpio) 135 continue; 136 //同樣的,這里也是釋放 137 irq?=?gpio_to_irq(buttons[i].gpio); 138 free_irq(irq,?(void *)&buttons[i]); 139"white-space:pre"> //刪除一個定時器 140 del_timer_sync(&buttons[i].timer); 141 } 142 143 return 0; 144} 145//讀取按鍵的鍵值函數(shù) 146static int tiny4412_buttons_read(struct?file?*filp, char __user?*buff, 147 size_t count, loff_t *offp) 148{ 149 unsigned long err; 150 151 if (!ev_press)?{ 152 if (filp->f_flags?&?O_NONBLOCK) 153 return -EAGAIN; 154 else //等待中斷的事件產(chǎn)生 155 wait_event_interruptible(button_waitq,?ev_press); 156 } 157 158 ev_press?= 0; 159 //將獲取到的鍵值返回到用戶空間 160 err?=?copy_to_user((void *)buff,?(const void *)(&key_values), 161 min(sizeof(key_values),?count)); 162 163 return err???-EFAULT?:?min(sizeof(key_values),?count); 164} 165 166//按鍵非阻塞型接口設(shè)計 167static unsigned int tiny4412_buttons_poll(?struct?file?*file, 168 struct?poll_table_struct?*wait) 169{ 170 unsigned int mask?= 0; 171"white-space:pre"> //非阻塞型等待 172 poll_wait(file,?&button_waitq,?wait); 173 if (ev_press) 174 mask?|=?POLLIN?|?POLLRDNORM; 175 176 return mask; 177} 178 179//驅(qū)動文件操作結(jié)構(gòu)體成員初始化 180static struct file_operations dev_fops =?{ 181 .owner??????=?THIS_MODULE, 182 .open???????=?tiny4412_buttons_open, 183 .release????=?tiny4412_buttons_close, 184 .read???????=?tiny4412_buttons_read, 185 .poll???????=?tiny4412_buttons_poll, 186}; 187//注冊雜類設(shè)備的結(jié)構(gòu)體成員初始化 188static struct miscdevice misc =?{ 189 .minor??????=?MISC_DYNAMIC_MINOR, 190 .name???????=?DEVICE_NAME, 191 .fops???????=?&dev_fops, //這里就是把上面那個文件操作結(jié)構(gòu)體的成員注冊到雜類操作這里 192}; 193//按鍵驅(qū)動初始化 194static int __init button_dev_init(void) 195{ 196 int ret; 197 //先注冊一個雜類設(shè)備 198 //這相當(dāng)于讓misc去管理open?,read,write,close這些接口 199 ret?=?misc_register(&misc); 200 // 201 printk(DEVICE_NAME"\tinitialized\n"); 202 203 return ret; 204} 205//按鍵驅(qū)動注銷 206static void __exit button_dev_exit(void) 207{ 208 //注銷一個雜類設(shè)備驅(qū)動 209 misc_deregister(&misc); 210} 211 212module_init(button_dev_init); 213module_exit(button_dev_exit); 214 215MODULE_LICENSE("GPL"); 216MODULE_AUTHOR("Yang.yuanxin");

運行結(jié)果:




免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺僅提供信息存儲服務(wù)。文章僅代表作者個人觀點,不代表本平臺立場,如有問題,請聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
關(guān)閉