<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>

新聞中心

EEPW首頁(yè) > 消費電子 > 設計應用 > 基于嵌入式Linux的設備驅動(dòng)程序設計

基于嵌入式Linux的設備驅動(dòng)程序設計

——
作者: 時(shí)間:2007-11-17 來(lái)源:?jiǎn)纹瑱C與嵌入式系統應用 收藏

  為是一個(gè)成熟而穩定的。將植入嵌入式設備具有眾多的優(yōu)點(diǎn),包括可剪裁和容易移植等,所以在嵌入式領(lǐng)域獲得了廣泛的應用。嵌入式Linux一直是嵌入式領(lǐng)域的研究熱點(diǎn),與PC架構不同,的硬件具有多樣性和差異性,的開(kāi)發(fā)需要對特定系統進(jìn)行硬件設計,同時(shí)還要針對這些硬件來(lái)編寫(xiě)。Linux內核就是通過(guò)來(lái)同外圍設備打交道的,系統設計人員必須為每個(gè)設備編寫(xiě),否則設備無(wú)法在下正常工作。

  1 設備驅動(dòng)程序設計的基本概念與模型

  設備驅動(dòng)程序是操作系統內核與機器硬件之間的接口,它為應用程序屏蔽了硬件的細節,在應用程序看來(lái),硬件設備只是一個(gè)設備文件,應用程序可以象操作普通文件一樣對硬件設備進(jìn)行操作,設計驅動(dòng)程序是內核的一部分,可以實(shí)現以下功能:

  對設備初始化和釋放;

  把數據從內核傳送到硬件,以及從硬件讀取數據;

  讀取應用程序傳送給設備文件的數據,以及回送應用程序請求的數據;

  檢測和處理設備出現的錯誤。

  前面已經(jīng)提到驅動(dòng)程序的作用,而編寫(xiě)驅動(dòng)程序就是構造一系列可供應用程序調動(dòng)的函數(包括open、release、read、write、llseek、ioctl等)。在用戶(hù)自己的驅動(dòng)程序中,首先要根據驅動(dòng)程序的功能,實(shí)現file_operations結構中的函數,不需要的函數接口可以直接在file_operations結構中初始化為NULL;file_operations變量會(huì )在驅動(dòng)程序初始化時(shí)注冊到系統內部。當操作系統對設備操作時(shí),會(huì )調用驅動(dòng)程序注冊的file_operations結構中的函數指針。

  以下是嵌入式linux2.4設備驅動(dòng)程序的最簡(jiǎn)模型。

  

  

  具體實(shí)現前面定義的函數時(shí),需注意下面幾點(diǎn):

  1)在test_init函數中要通過(guò)調用register_chrdev()函數來(lái)向內核注冊字符設備驅動(dòng)程序。如果是塊設備,則還需調用mmmap()進(jìn)行地址空間的映射,再調用register_blkdev()函數來(lái)向內核注冊塊設備驅動(dòng)程序,在Linux系統中,對中斷的處理是屬于系統核心部分,因而如果設備與系統之間以中斷方式進(jìn)行數據交換,則必須把該設備的驅動(dòng)程序作為系統核心的一部分,也就是說(shuō)設備驅動(dòng)程序要通過(guò)調用request_irq()函數來(lái)申請中斷,通過(guò)free_irq()函數來(lái)釋放中斷(在test_cleanup中實(shí)現)。

  2)open()函數和release()函數的具體實(shí)現有著(zhù)一定的對應性,在open()函數中主要是執行打開(kāi)設備時(shí)的一些初始化代碼,如果該驅動(dòng)程序需要管理多個(gè)設備,那么還要獲取從設備號,根據從設備號來(lái)判斷需要操作的設備,其中,從設備號可通過(guò)調用函數MINOR(inode->i_rdev)來(lái)獲得,然后再調用宏MOD_INC_USE_COUNT來(lái)使得驅動(dòng)程序使用計數器加1,而在release()函數中則要進(jìn)行相反的處理。即調用宏MOD_DEC_USE_COUNT來(lái)減小驅動(dòng)程序使用計數器。

  3)歸根到底,驅動(dòng)函數的實(shí)現就是調用內核所支持的函數(包括內核提供的API和用戶(hù)自己定義的寄存器操作函數)來(lái)完成對設備的操作,雖然設備的種類(lèi)眾多,不同設備操作的具體實(shí)現方法不可能相同,但是Linux操作系統提供了一系列特殊API,為開(kāi)發(fā)內核驅動(dòng)程序帶來(lái)了很大的方便,調用這些API時(shí)需要注意的是:通常情況下,應用程序是通過(guò)內核接口訪(fǎng)問(wèn)驅動(dòng)程序的(這是驅動(dòng)程序的主要使用方式),因此驅動(dòng)程序需要與應用程序交換數據,但是操作系統內核和驅動(dòng)程序在內核空間中運行,而用戶(hù)程序在用戶(hù)空間中運行,用戶(hù)程序不能訪(fǎng)問(wèn)內核空間,操作系統內核和驅動(dòng)程序也不能使用指針或memcpy()等常規的C庫函與用戶(hù)空間傳輸數據,造成這種狀況的主要原因是linux操作系統使用了虛擬內存機制,使用了虛擬內存機制后,用戶(hù)空間的內存可能被換出,當內核使用用戶(hù)空間指針時(shí),對應的頁(yè)面可能已經(jīng)不在內存中了,因此在使用調用函數時(shí)要注意:設備驅動(dòng)程序在申請和釋放內存時(shí)不是調用malloc()和free(),而調用kmalloc()和kfree();用于內核空間與用戶(hù)空間進(jìn)行數據拷貝的函數主要有access_ok()(檢查某內存空間是否有權訪(fǎng)問(wèn)),copy_to_user()和put_usr()(內核函數向用戶(hù)空間傳輸數據),copy_from_user()和get_user()(用戶(hù)空間向內核空間傳輸數據);關(guān)于內核空間與I/O空間的數據交換,不同體系結構的處理器對I/O的處理方式也不同,x86系列處理器中,I/O與內存完成不同,它是分開(kāi)編址的,訪(fǎng)問(wèn)它要使用專(zhuān)用的指令;而對ARM體系結構的處理器來(lái)說(shuō),則是不區分I/O和內存,統一編址的,可以使用同樣的指令訪(fǎng)問(wèn),在驅動(dòng)程序中可以使用一系列函數來(lái)訪(fǎng)問(wèn)I/O口,如outb()、outw()、outl()inb()、inw()、inl()、outsb()、outsw()、outsl()、insb()、insw()和insl()等。

  2 Linux2.6與2.4內核驅動(dòng)程序的區別

  為了徹底防止對正在被使用的內核模塊進(jìn)行錯誤操作,linux2.6內核在加載和導出內核模塊方面都較2.4內核有所改進(jìn),避免了用戶(hù)執行將導致系統崩潰的操作(例如強制刪除模塊等)。同時(shí),當驅動(dòng)程序需要在多個(gè)文件中包含頭文件時(shí),不必定義宏來(lái)檢查內核的版本。與2.4內核相比,2.6內核在可擴展性、吞吐率等方面有較大提升,其新特性主要包括:使用了新的調度器算法;內核搶占功能顯著(zhù)地降低了用戶(hù)交互式應用程序;多媒體應用程序等類(lèi)似應用程序的延遲;改進(jìn)了線(xiàn)程模型以及對NPTL的支持,顯著(zhù)改善了虛擬內存在一定成程度負載下的性能;能夠支持更多的文件系統;引進(jìn)了內存池技術(shù);支持更多的系統設備,在2.4內核中有約束大型系統的限制,其支持的每一類(lèi)設備的最大數量為256,而2.6內核則徹底打破了這些限制,可以支持4095種主要的設備類(lèi)型,且每個(gè)單獨的類(lèi)型又可以支持超過(guò)一百萬(wàn)個(gè)的子設備;支持反向映射機制(reverse mapping),內存管理器為每一個(gè)物理頁(yè)建立一個(gè)鏈表,包含指向當前映射頁(yè)中每個(gè)進(jìn)程的頁(yè)表條目的指針。該鏈表叫PTE鏈,它極大的提高了找到那些映射某個(gè)頁(yè)的進(jìn)程的速度。

  Linux操作系統的設備驅動(dòng)程序是在內核空間運行的程序,其中涉及很多內核的操作,隨著(zhù)Linux內核版本的升級,驅動(dòng)程序的開(kāi)發(fā)必然也要作出相應的修改,總之,在linux2.6內核上編寫(xiě)設備驅動(dòng)程序時(shí)具體要注意以下幾個(gè)方面:

  1)Linux2.6內核驅動(dòng)程序必須由MODULE_LICENSE("Dual BSD/GPL")語(yǔ)句來(lái)定義許可證,而不能再用2.4內核的MODULE_LICENSE("GPL")。否則,在編譯時(shí)會(huì )出現警告提示。

  2)Linux2.6內核驅動(dòng)程序可以用int try_module_get(&module)來(lái)加載模塊,用module_put()函數來(lái)卸載模塊,而以前2.4內核使用的宏MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT則可不用。

  3)前面給出的字符型設備驅動(dòng)程序模型中結構體file_operations的定義要采用下面的形式。這是因為在Linux內核中對結構體的定義形式發(fā)生了變化,不再支持原來(lái)的定義形式。

  

  4)就字符型設備而言,test_open()函數中向內核注冊設備的調用函數register_chrdev()可以升級為int register_chrdev_region(dev_t from,unsigned count,char * name),如果要動(dòng)態(tài)申請主設備號可調用函數int alloc_chrdev_region(dev_t * dev,unsigned baseminor,unsigned count,char * name)來(lái)完成;原來(lái)的注冊函數還可以用,只是不能注冊設備號大于256的設備,同理,對于塊設備和網(wǎng)絡(luò )設備的注冊函數也有著(zhù)相對應的代替函數。

  5)在聲明驅動(dòng)程序是否要導出符號表方面有著(zhù)很大的變化。當驅動(dòng)程序模塊裝入內核后,它所導出的任何符號都會(huì )變成公共符合表的一部分,在/proc/ksyms中可以看到這些新增加的符號。通常情況之下,模塊只需實(shí)現自己的功能,不必導出任何符號,然而,如果有其他模塊需要使用模塊導出的符號時(shí),就必須導出符號,只有顯示的導出符號才能被其他模塊使用,Linux2.6內核中默認不導出所有的符號,不必使用EXPORT_NO_SYMBOLS宏來(lái)定義;而在2.4內核中恰恰相反,它默認導出所有的符號,除非使用EXPORT_NO_SYMBOLS,因此在上面給出的范例中可以省略去該定義語(yǔ)句。

  6)Linx內核統一了很多設備類(lèi)型,同時(shí)也支持更大的系統和更多的設備,原來(lái)Linux2.4內核中的變量kdev_t已經(jīng)被廢除不可用,取而代之的是dev_t。它拓展到了32位,其中包括12位主設備號和20位次設備號。調用函數為unsigned int iminor(struct inode * inode)和unsigned int imajor(struct inode * inode),而不再用Linux2.4版本中的int MAJOR(kdev_t dev)和int MINOR(kdev_t dev)。

  所有的內存分配函數不再包含在頭文件中,而是包含在中,而原來(lái)的已經(jīng)不存在。所以當在驅動(dòng)程序中要用到函數kmalloc()或kfree()等內存分配函數時(shí),就必須要定義頭文件而不是。同時(shí),前面提到的申請內存和釋放內存函數的具體參數也有了一定的改變,包括:分配標志GFP_BUFFER被取消,取而代之的是GFP_NOIO和GFP_NOFS;新增了_GFP_REPEAT、_GFP_NOFAIL和_GFP_NORETRY分配標志等,使得內存操作更加方便。

  8)因為內核中有些地方的內存分配是不允許失敗的,所以為了確保這種情況下得成功分配,linux2.6版本內核中開(kāi)發(fā)了一種稱(chēng)為"內存池"的抽象。內存池其實(shí)相當于后備的高速緩存,以便在緊急狀態(tài)下使用。要使用內存池的處理函數時(shí),必須包含頭文件。內存池處理函數主要有以下幾個(gè):mempool_t *mempool_create()、void*mempool_alloc()、void mempool_free()、int mempool_resize();

  另外值得一提的是:2.6內核為了區別以.o為擴展名的常規對象文件,將內核模塊的擴展名改為.ko,所以驅動(dòng)程序最后是被編譯為ko后綴的可加載模塊,在應用程序中加載驅動(dòng)程序模塊時(shí)要注意。 結語(yǔ)

  驅動(dòng)程序的開(kāi)發(fā)作為嵌入式linux系統開(kāi)發(fā)過(guò)程當中最重要的環(huán)節之一,與硬件特性和操作系統的內核有著(zhù)緊密的聯(lián)系。隨著(zhù)linux內核版本的升級,內核驅動(dòng)程序必然要作出相應的改進(jìn),相信隨著(zhù)嵌入式Linux系統在各個(gè)領(lǐng)域中的廣泛應用,具有可搶占實(shí)時(shí)性的Linux2.6內核必定會(huì )在嵌入式領(lǐng)域大顯身手。本文會(huì )對廣大的驅動(dòng)程序開(kāi)發(fā)人員有一定的幫助。

linux操作系統文章專(zhuān)題:linux操作系統詳解(linux不再難懂)


評論


相關(guān)推薦

技術(shù)專(zhuān)區

關(guān)閉
国产精品自在自线亚洲|国产精品无圣光一区二区|国产日产欧洲无码视频|久久久一本精品99久久K精品66|欧美人与动牲交片免费播放
<dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><small id="yhprb"></small><dfn id="yhprb"></dfn><small id="yhprb"><delect id="yhprb"></delect></small><small id="yhprb"></small><small id="yhprb"></small> <delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"></dfn><dfn id="yhprb"></dfn><s id="yhprb"><noframes id="yhprb"><small id="yhprb"><dfn id="yhprb"></dfn></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><small id="yhprb"></small><dfn id="yhprb"><delect id="yhprb"></delect></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn> <small id="yhprb"></small><delect id="yhprb"><strike id="yhprb"></strike></delect><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn><dfn id="yhprb"><s id="yhprb"><strike id="yhprb"></strike></s></dfn><dfn id="yhprb"><s id="yhprb"></s></dfn>