linux內(nèi)核驅(qū)動(dòng)應(yīng)用三合一_第1頁
linux內(nèi)核驅(qū)動(dòng)應(yīng)用三合一_第2頁
linux內(nèi)核驅(qū)動(dòng)應(yīng)用三合一_第3頁
linux內(nèi)核驅(qū)動(dòng)應(yīng)用三合一_第4頁
linux內(nèi)核驅(qū)動(dòng)應(yīng)用三合一_第5頁
已閱讀5頁,還剩24頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

?,Linux???N)???h?ǜ????,Linux???N)???h?ǜ???1o??1o??1oI/Oh???h???h?ǘ?ě???1??h?±I/O?ǐ???ph?±I/O???h?±I/O??;?|"???h?ǒ?I/O???Linux?h?ǜ?h????r?ú?h??ǖh???Linuxh??????11.4?????I/Oh?±I/O?ǐ?11.5第章內(nèi)存與I/OLinux#4%解(第2版 內(nèi)存空0與I/O空?X86??2???I/Oǒ???ěI/Oǒ???a,h?ǒ????????ú??8inLinux#4%解(第2版 內(nèi)存空0與I/O空?X86??2???I/Oǒ???ěI/Oǒ???a,h?ǒ????????ú??8inout?ǖ??)?????21oel???inout?8????|2?Cǘ??NI/Oǒ??}h?ǒ??9????1oǐā±ǐā??h?1o?9???C?????JM??186??2???jb??Cǘ????1o???2p?¨?h?ǒ?ê?0xF000FF00*p=11ê???1ojbi?1860!?1o0?9ǐā?ūú%<Iq??HE?ǐā???)???q???lpReset()????{)????J???{CPU??|ǘ??????8?F?′??9??q??????ūq?H?q??????1o?????P??X86??2???N)I/Oǒ??????????I/Oǖ?????ǔ???h?ǒ???CPU?I/O?8′?h?ǒ??????I/Oǒ?????·11.1?p)h?ǒ?±I/Otypedefvoid(*lpFunction)();/*ú%?????3ǜ??q???ǜ?*/lpFunctionlpReset=(lpFunction)0xF000 0;/*ú%q????¨*//*CPU??|???ǘ??8?F?lpReset();/*??q?unsignedchar*p=(unsignedcharIN??2, OUT CPU內(nèi)存與I/O幎in、端I/O空間(可選內(nèi)存空間(必須11.1內(nèi)存空梃I/O 內(nèi)存管理高性能處理器一般會(huì)提供一個(gè)內(nèi)存管內(nèi)存與I/O幎in、端I/O空間(可選內(nèi)存空間(必須11.1內(nèi)存空梃I/O 內(nèi)存管理高性能處理器一般會(huì)提供一個(gè)內(nèi)存管理單元(MMU,該單元輔助操作系統(tǒng)進(jìn)行內(nèi)存管理提供虛擬地址和物理地址、內(nèi)權(quán)限保護(hù)和Cache緩存控制等硬件支持。操作系統(tǒng)核借助MMU,可以讓用戶感覺到好像程序可以使用非常大的內(nèi)存空間,從而使得編程程序時(shí)不用考慮計(jì)算機(jī)中的物理內(nèi)存的實(shí)際容量。為了理解基本的MMU操作原理,需先明晰幾個(gè)概念(1)TLB:TranslationLookasideBuffer,即轉(zhuǎn)換旁路緩存,TLB是MMU在,緩存少量的虛擬地址與物理地址的轉(zhuǎn)換關(guān)系,是轉(zhuǎn)換表的Cache,因此也經(jīng)常被稱為“快表應(yīng)的地址轉(zhuǎn)換關(guān)時(shí),需要通過對所示)來得虛擬地址和物理地址的對應(yīng)關(guān)系。TTW成功后,結(jié)果應(yīng)寫入TLB11.2內(nèi)存中的懻?11.3給出了一個(gè)典型ARM處理要器時(shí),MMU先查找TLB中的虛擬地址表。如果ARM的結(jié)構(gòu)支持分開的數(shù)據(jù)(DTLB)和指令TLB(ITLB,則除取指令使用ITLB外,其他的都使用DTLB。ARM處理器MMU11.3所示第章外內(nèi)外Linux#4%解(第2版11.3ARM的內(nèi)存管理Linux#4%解(第2版11.3ARM的內(nèi)存管理?TLB2???? ?????TTW¤???.R??bTLB?ī????ī???&|?TLB???|R???|]?? ??Cacheī??h????·11.411.4ARMCPU,行數(shù)據(jù)$/的流內(nèi)存與I/OARM?TLB????9?Cache!C????±B?kF????|a?1o?????±j?k內(nèi)存與I/OARM?TLB????9?Cache!C????±B?kF????|a?1o?????±j?k!?a?]???]?vMMU?¨ARM2?ú????MMU?|?a????ARM??2CMIPSMMUfū??1o±??1o?? ??Q?ǘ????L?Linux???!?????īǒ?h?ǒ??1oLinuxh?L?)???PGDPMD±PTEa,??H?????PMD? LinuxCGYO第章 walk_page_tables(structmm_structunsignedlongpte_t*pte_ret)4{pgd_tpmd_tpte_t#ifdefHAVEPUDpud_tpgd=pgd_offset(mm,if(pgd_none(*pgd)||gotoout;#ifdefpud=pud_offset(pgd,if(pud_none(*pud)||gotoout;pmd=pmd_offset(pud,pmd=pmd_offset(pgd,if(pmd_none(*pmd)||gotoptep=pte_offset_map(pmd,ifgotoout;*pte_ret=Linux#4%解(第2版ǘ1??ǜ?!structmm_struct???mm?,??Linuxɑǐ??ū?h?????pgd_offsetpmd_offsetrz?,?{ū|??Linux#4%解(第2版ǘ1??ǜ?!structmm_struct???mm?,??Linuxɑǐ??ū?h?????pgd_offsetpmd_offsetrz?,?{ū|??E?MMU??a?ū??2?????M?ú??SAMSUNG?,ARM7TDMI?S3C44B0X??MMU???Linux2.6ō??MMU???2?????£)Clinux?é??MMU???2Linux2.6.MMU-less???DragonballColdFireHitachiH8/300Blackfina,??MMU???2??Linux???N)????h??{4GB?Linux??ɑǐ?4GBh?ǒ??r!?r?īǒ?h?ǒ?1o?r?!03GB?PAGE_OFFSET?0x86?ǘ,0xC0000000??~?ǜ???4GB!h?ǒ??·11.5???īɑǐ?ú?m?īǒ????1oh?ǒ???1o?īɑǐ?ū??????7??īɑǐ?h????11.5用空0與內(nèi)核空?ɑǐ??īǒ????c?ǔ-?y??īɑǐ¢?ū¤?????A??ɑǐ????ú?h?ǒ?1oū??a????Linux1GB???h?r????o???·11.6??m?ūé??!896MB????????h?é,896MB???h??ú?ǎ!ú?h?LinuxQ?h?ǒ?ū??FIXADDR_TOP4GBLinuxreturnreturn-1;37}內(nèi)存與I/O幎11.6Linux內(nèi)核地址空緊接著最頂端的保留區(qū)以下的一段區(qū)域頁內(nèi)存與I/O幎11.6Linux內(nèi)核地址空緊接著最頂端的保留區(qū)以下的一段區(qū)域頁可獲區(qū)內(nèi)預(yù)定義頁面的邏輯地址。其開始地址和結(jié)束地址宏定義如下接下來,如果系統(tǒng)配置了高端內(nèi)存,則位其起始地址為PKMAP_BASE,定義如下頁區(qū)之下的就是一段高端內(nèi)區(qū)其中所涉在物理區(qū)和高 區(qū)之間為虛存內(nèi)存分配區(qū)(VMALLOC_START~VMALLOC_END用于vmalloc()函數(shù)它的前部與物理內(nèi)vmalloc區(qū)域定義如下區(qū)有一后部與高區(qū)也有一帶當(dāng)系統(tǒng)物理內(nèi)存超過4GB時(shí),必須使用CPU的擴(kuò)展分頁(PAE)模式所提供的64位頁目項(xiàng)才能存取到4GB以上的物理內(nèi)存,這需要CPU的支持。加入了PAE功能elPentium及其后的CPU允許內(nèi)存最大可配置到64GB,具備36位物理地址空間尋址能力第章#defineVMALLOC_OFFSET#defineVMALLOC_START(((unsignedlong)high_memory+vmalloc_earlyreserve+2*VMALLOC_OFFSET-1)&~(VMALLOC_OFFSET-1))#ifdef /*支持高端內(nèi)存#defineVMALLOC_END(PKMAP_BASE- /*不支持高端內(nèi)存#defineVMALLOC_END(FIXADDR_START-#defineFIXADDRBOOTSTART(FIXADDRTOP FIXADDRBOOT#defineLAST_PKMAP #definePTRS_PER_PTE512#definePMD_MASK(~(PMD_SIZE-#definePMD_SIZE(1UL<<#definePMD_SHIFT#define KMAP+1))&PMD_MASK#define (FIXADDR_TOP-_#defineFIXADDR_TOP ((unsignedlong)__FIXADDR_TOP)#define__FIXADDR_TOP0x Linux#4%解(第2版?????34GB&??h?ǒ?5G1o{?1oO?!? ? 用Linux#4%解(第2版?????34GB&??h?ǒ?5G1o{?1oO?!? ? 用空0內(nèi)存申??īǒ?????h??q?!malloc()????L??q?!malloc()?h?ú??free()avA??h??????mmalloc()±p?????M????????function()??function()?????h?|?efree?7??£??)malloc()±free()?ap???v????v?7???£??é′!?ī??????Z????????h??Bbfunction()q???q?function()v?ō??p??c?malloc()±free()?ap?ū?Uē?Z{?P??(?ì???|?????μ&h???A?????h???Ba,Linuxh???C??malloc()q??ú??brk()±m(xù)map()char*q=voidfunction(char{.../*?u?ap??J}char*p=malloc(…);char*{charp=(char*)malloc(…);.../*?u?ap??J*/returnp;}內(nèi)存與I/O幎 內(nèi)核空梃內(nèi)存┷日在Linux內(nèi)核空間申請內(nèi)存涉及的函數(shù)主要包括kmalloc()_內(nèi)存與I/O幎 內(nèi)核空梃內(nèi)存┷日在Linux內(nèi)核空間申請內(nèi)存涉及的函數(shù)主要包括kmalloc()__get_free_pages()和vmalloc()等kmalloc()和__get_free_pages()(及其類似函數(shù))申請的內(nèi)存位于物理內(nèi)區(qū)域,而且在物上也是連續(xù)的,它們與真實(shí)的物理地址只有一個(gè)固定的偏移,因此存在較簡單的轉(zhuǎn)換關(guān)系。而vmalloc()在虛擬內(nèi)存空間給出一塊連續(xù)的內(nèi)存區(qū),實(shí)質(zhì)上,這片連續(xù)的虛擬內(nèi)存在物理內(nèi)存中并不一定連續(xù),而vmalloc()申請的虛擬內(nèi)存和物理內(nèi)存之間也沒有簡單的換算關(guān)系。給kmalloc()的第一個(gè)參數(shù)是要分配的塊的大小,第二個(gè)參數(shù)為分配標(biāo)志,用于控制的行為最常用的分配標(biāo)志是GKRNm的底層依賴__get_free_pages()實(shí)現(xiàn),分配標(biāo)志的前綴GFP正好是這個(gè)底層函數(shù)的縮寫。使用GFP_KERNELGFP_KERNE申請內(nèi)存。在中斷處理函數(shù)、tasklet和內(nèi)核定時(shí)器等非進(jìn)程上下文中不能阻塞,此時(shí)驅(qū)動(dòng)應(yīng)當(dāng)使用GFP_ATOMIC標(biāo)志來申請內(nèi)存。當(dāng)使用GFP_ATOMIC其他的相對不常用的申請標(biāo)志還包括GFP_USER(用來為用戶空間頁分配內(nèi)存,可能阻塞GFP_HIGHUSER(類似GFP_USER,但是從高端內(nèi)存分配、GFP_NOIO(不允許任何I/O初化、存區(qū)、_要求分配在能DMA的GPCOLD(請求一個(gè)(高優(yōu)先級請求,允許獲得被內(nèi)核保留給緊急狀況使用的最后的內(nèi)存頁、__GFP_REPEAT(分配不到,則立即放棄使用kmalloc()申請的內(nèi)存應(yīng)使用2.__get_free_pages,這個(gè)函數(shù)的用法和用戶空間的free()類似__get_free_pages()系列函數(shù)/宏是Linux內(nèi)核本質(zhì)上最底層的用于獲取空閑內(nèi)存的方法,的管理空閑內(nèi)存,所以最底層的內(nèi)存申請總是以頁__get_free_pages()系列函數(shù)/宏包括get_zeroed_page()、__get_free_page()和__get_free_pages()該函數(shù)返回一個(gè)指向新頁的指針并且將該頁清零該宏返回一個(gè)指向新頁的指針但是該頁不清零,它實(shí)際上為就是調(diào)用了下面的__get_free_pages()申請1頁第章#define__get_free_page(gfp_mask)__ void*kmalloc(size_t Linux#4%解(第2版_flags,?q??r?????3r?h???1or????!2orderr???(??]??ūéW?10?1024?ī?11?Linux#4%解(第2版_flags,?q??r?????3r?h???1or????!2orderr???(??]??ūéW?10?1024?ī?11?2048?__get_free_pages()±get_zeroed_page()?????)aoc_pages()q?9?h?ǒ?r?(?9??īǒ?r????%get_free_pages()ǜDE??3r??ǘL?get_free_pages()?uq?/ù???h??L?如果申&和-order不一,會(huì)引起內(nèi)存。get_free_pagesǘq??L??e?????Wkmalloc()?c?¢????%(kmalloc()?c?ūú???GFP_KERNEL±GFP_ATOMIC3vmalloc()?ūa???;?%vmalloc()?é,__get_free_pages()???!)??vmalloc()????????ǔ′???vmalloc()?r?è??h??1??vmalloc()???h??L?vmalloc()±voidvmalloc()P_KERNEL?L?vmallocq??Mòq??create_module()?????y?vmalloc()q????4slab??h?Y??????????è?ó?(??1????ǐ?úA??é?a??????h???Linux????{?a???g??Mò?inodetask_structǘL??a?}|??L??r??¤?h?ī¤ǜh?ǒ?Q?)???????ê?1w?slabkmem_cache_create()?,w?slab?????9??<???c?¤?é??|?????size??r????????é???flags??|?Iɑ?r??Fń?SLAB_NO_REAP?Lh???(??ō?????SLAB_HWCACHE_ALIGN?structkmem_cache*kmem_cache_create(constchar*name,size_tsize,size_talign,unsignedlongflags,void(*ctor)(void*,structkmem_cache*,unsignedlong),void(*dtor)(void*,structkmem_cache*,unsignedlong));void*vmalloc(unsignedlongvoidfree_page(unsignedlongvoidfree_pages(unsignedlongaddr,unsignedlongstructpage* gfp_mask,unsignedlong內(nèi)存與I/O幎(2)分slab緩存上述函數(shù)在kmem_cache_create()創(chuàng)建的slab后備緩沖中分內(nèi)存與I/O幎(2)分slab緩存上述函數(shù)在kmem_cache_create()創(chuàng)建的slab后備緩沖中分配一塊并返回首地址指針slab緩存上述函由kmem_cache_alloc()分配的緩存(4)收slab緩存代11.2給出slab緩存的使用范例代 slab緩存使用范在系統(tǒng)中通過/proc/slabinfo結(jié)點(diǎn)可以獲知當(dāng)前slab的分配和使用情況,例如在LDD6410開板上運(yùn)行“cat/注意,slab不是要代替__get_free_pages(),其在最底層仍然依賴于_在底層每次申請1頁或多頁,之后再分隔這些頁為更小的單元進(jìn)行管理,從而節(jié)省了內(nèi)存,也slab緩沖對象效率除了slab以外,在Linux內(nèi)核中還包含對內(nèi)存池的支持,內(nèi)存池技術(shù)也是一種非常經(jīng)典的于分配大量小對象的后備緩存技術(shù)第章/*slab緩存 ickmem_cache_t _cachep= ", 0,SLAB_HWCACHE_ALIGN|SLAB_PANIC,NULL,/*slab緩存 ctx= _cachep,.../*slab slab緩存 _cachep, kmem_cache_destroy(structkmem_cachevoidkmem_cache_free(structkmem_cache*cachep,voidvoid*kmem_cache_alloc(structkmem_cache*cachep,gfp_tLinux#4%解(第2版Linuxh?h???d??J???1mempool_create()q??,w?h??min_nr??????r?a????±free_fn??¨h???|?N??na?r?±3ōq????±pool_data?r?±3ōq??{???Linux#4%解(第2版Linuxh?h???d??J???1mempool_create()q??,w?h??min_nr??????r?a????±free_fn??¨h???|?N??na?r?±3ōq????±pool_data?r?±3ōq??{???gfp_mask?r????ū?__GFP_WAIT????ú?r?q??A>?2?h??r?±3ōa???9mempool_alloc()??r?a???h??r?2???Nh?3mempool_create()q?w??h???? 虛地址與物理地址????h?L??1ovirt_to_phys()????H????d?a,ARM??11.3 virt_to_phys()????ǐ?PAGE_OFFSET?ú!3GB?PHYS_OFFSETvú,!??DRAM?o′?a,LDD6410????????0{3GB??????SDRAM{&a??q?!phys_to_virt()????1o??!h???1o11.4 phys_to_virt()h?1s icinlinevoid*phys_to_virt(unsignedlongx)2{ return(void*)(phys_to_virt((unsignedlong)(x)));4}5 ((x)-PHYS_OFFSET+1s icinlineunsignedlongvirt_to_phys(void*x)2{ virttophys((unsignedlong)(x));4}5#define_ ((x)_OFFSET+voidmempool_destroy(mempool_tvoid*mempool_alloc(mempool_t*pool, voidmempool_free(void*element,mempool_t*pool);typedefvoid(mempool_free_t)(void*element,voidtypedefvoid gfp_mask,voidmempool_t*mempool_create( min_nr,mempool_alloc_t*alloc_fn,mempool_free_t*free_fn,void*pool_data);內(nèi)存與I/O幎虛擬地址與物理地址之間不存在如此簡單的換算關(guān)系設(shè)備通常會(huì)提供一組寄存器來用于控制設(shè)備、讀寫設(shè)備和獲取設(shè)備狀態(tài),即控制寄存器、數(shù)據(jù)寄存器和狀態(tài)寄存器。這些寄存器可能位于I/O空間,也可能位于內(nèi)存空間。當(dāng)位于I內(nèi)存與I/O幎虛擬地址與物理地址之間不存在如此簡單的換算關(guān)系設(shè)備通常會(huì)提供一組寄存器來用于控制設(shè)備、讀寫設(shè)備和獲取設(shè)備狀態(tài),即控制寄存器、數(shù)據(jù)寄存器和狀態(tài)寄存器。這些寄存器可能位于I/O空間,也可能位于內(nèi)存空間。當(dāng)位于I/O空間時(shí),通常被稱為I/O端口,位于內(nèi)存空間時(shí),對應(yīng)的內(nèi)存空間被稱為I/O內(nèi)存。LinuxI/O端口和I/O內(nèi)存幎桽接I/O端Linux設(shè)備驅(qū)動(dòng)中,應(yīng)使Linux內(nèi)核提供的函數(shù)來定位I/O空間的端口,這些函(1)讀寫字節(jié)端口(8位寬(2)讀寫字端口(16位寬(3)讀寫長字端口(32位寬(4)讀寫(5)insb()從端口port開始count個(gè)字節(jié)端口,并結(jié)果寫addr指向的內(nèi)存addr指向的內(nèi)存count個(gè)字節(jié)連續(xù)地寫port開始的端口(6)讀寫一串字(7)讀寫一串長字上述各函數(shù)I/O2.I/O內(nèi)port的類型高度依賴于具體的硬,因此,只是寫出unsigned在內(nèi)核 I/O內(nèi)存之前,需首先使用ioremap()函數(shù)將設(shè)備所處的物理地址。ioremap()的原型如下到虛擬ioremap()與vmalloc()類似,也需要建立新的頁表,但是它并不進(jìn)行vmalloc()中所執(zhí)行的內(nèi)分配行為。ioremap()返回一個(gè)特殊的虛擬地址,該地址可用來存取特定的物理地址范圍。通ioremap()獲得的虛擬地址應(yīng)該被iounmap()函,其原型如下第章voidiounmap(void*void*ioremap(unsignedlongoffset,unsignedlongvoidins signedport,void*addr,unsignedlongcount);voidouts signedport,void*addr,unsignedlongcount);voidinsw(unsignedport,void*addr,unsignedlongcount);voidoutsw(unsignedport,void*addr,unsignedlongcount);void voidoutsb(unsignedport,void*addr,unsignedlongunsigned signedvoid signedlongword,unsignedunsignedinw(unsignedvoidoutw(unsignedshortword,unsignedunsignedinb(unsignedvoidoutb(unsignedcharbyte,unsigned幍?I/O端口和I/OLinux#4%解(第2版{??1o&|?.1oL?Linuxh???1?I/O???1o??j?.q???q?a???????q?!?.q??Linux2.62jLinux#4%解(第2版{??1o&|?.1oL?Linuxh???1?I/O???1o??j?.q???q?a???????q?!?.q??Linux2.62jI/O?q?a???????q?!?.q??Linux2.63?I/O4jI/O6??I/O3)I/O???q??9?port???count???I/O!?h?ǒ?ê?9?e?3?1o??I/Oh??.I/Oǖ?????r?{h?ǒ??!?? ?X?{h???1o?I/O??I/O 申&與-放#I/O端口I/O內(nèi)1I/OFLinuxh??N)I/Ostructresource*request_region(unsigned ,unsignedlongn,constcharvoidioport_unmap(voidvoid*ioport_map(unsignedlongport, voidmemset_io(void*addr,u8value, voidmemcpy_fromio(voiddest,voidsource,unsigned voidmemcpy_toio(void*dest,void*source,unsigned voidiowrite8_rep(void*addr,constvoid*buf,unsignedlongcount);voidiowrite16_rep(void*addr,constvoid*buf,unsignedlongcount);void gvoidioread8_rep(void*addr,void*buf,unsignedlongcount);voidioread16_rep(void*addr,void*buf,unsignedlongcount);voidioread32_rep(void*addr,void*buf,unsignedlongvoidwriteb(unsignedvalue,address);voidwritew(unsignedvalue,address);voidwri(unsignedvalue,voidiowrite8(u8value,void*addr);voidiowrite16(u16value,void*addr);voidiowrite32(u32value,voidunsignedunsignedreadw(address);unsigned ioread8(void*addr); ioread16(void*addr); ioread32(void內(nèi)存與I/O?q?¨h???nǖ???name??!???¥ǎ???3W??NULL???3NULL??request_region()???I/Oǖ?L???|??L?release_region()q???:??????q????? void內(nèi)存與I/O?q?¨h???nǖ???name??!???¥ǎ???3W??NULL???3NULL??request_region()???I/Oǖ?L???|??L?release_region()q???:??????q????? voidrelease_region(unsignedlongstart,unsignedlong 2I/O¤?Linuxh?(?N)I/O structresource*request_mem_region(unsignedlongstart,unsignedlonglen,char?q?¨h???nh?1o??name??!???¥ǎr????3W??NULL???3NULLv?ˉ???I/O?:??????q????? voidrelease_mem_region(unsignedlongstart,unsignedlong ū??????ǐā??ū??I/Oǖ?±I/O)? #I/O端口I/O內(nèi)存$/流?£11.3?±???hYI/Oǖ?±I/OI/O?ǎ?????L?I/Oǖ??Jq?I/Oǖ???&|L?inb()ǖ??μ?ū|11.7I/O端口的$/流程(不到內(nèi)存空I/O??ǎ????I/O ???I/Oǖ????L?{h?&|L?I/Oū| I/O??ǐ?·11.8I/O???·11.9???`???request_mem_region()????21o??{h?ǒ???1o&|ê?9??Linux ???|?a region()???I/O第章Linux#4%解(第2版11.8I/O端口的$/流程到內(nèi)存空11.9I/O內(nèi)存$/流 將地到用空??m?īǒ??Linux#4%解(第2版11.8I/O端口的$/流程到內(nèi)存空11.9I/O內(nèi)存$/流 將地到用空??m?īǒ????(???E?????ǐāmmap()q???????1o???ǐ???īǒ???h???h?d?μ???????mmap()??9PAGE_SIZE??5file_operations?;?J??H?9?p?? (*mmap)(structfile*,struct ???mmap()q????īɑ?mmap()?????ū????file_operationsmmap()????zēé? caddr_tmmap(caddr_taddr,size_tflags, fd,off_toffset);??fd!?;??ǖ??open()?3fd(?9?ú!!1????úflags??MAP_ANONlen?;?ìoffsetó???ǚ?offset{???īǒ??ó?????!0prot?????oW?īPROT_READ??PROT_WRITEjPROT_EXEC???±PROT_NONE??addr{?īǒ????1o???ú!NULL??1o?<???h????void{?īǒ??1oeǜ?caddr_t??內(nèi)存與I/O幎當(dāng)用戶調(diào)用mmap()的時(shí)候,內(nèi)核會(huì)進(jìn)行如下處理①②③④在進(jìn)程的虛擬空間查內(nèi)存與I/O幎當(dāng)用戶調(diào)用mmap()的時(shí)候,內(nèi)核會(huì)進(jìn)行如下處理①②③④在進(jìn)程的虛擬空間查找一塊VMA將這VMA進(jìn)。如果設(shè)備驅(qū)動(dòng)程序或者文件系統(tǒng)的file_operations定義了mmap()操作,則調(diào)用它將這進(jìn)程VMA鏈表中file_operations中mmap()函數(shù)的第一個(gè)參數(shù)就是步驟①中找到的VMA由mmap()系統(tǒng)調(diào)的內(nèi)存可由munmap()解,這個(gè)函數(shù)的原型如下 munmap(caddr_taddr,size_tlen 驅(qū)動(dòng)程序mmap()的實(shí)現(xiàn)機(jī)制是建立頁表,并填VMA結(jié)構(gòu)體vm_operations_struct指針VMAvm_area_struct,用于描述一個(gè)虛擬內(nèi)存區(qū)域,VMA結(jié)構(gòu)體的定義如代11.5所示代 VMA結(jié)構(gòu)VMA結(jié)構(gòu)體描述的虛地址介于vm_start和vm_end之間,而其vm_ops成員指向這個(gè)的操作集。針對VMA的操作都被包含在vm_operations_struct結(jié)構(gòu)體中,vm_operations_struct構(gòu)體的定義如代11.6所示代 vm_operations_struct結(jié)構(gòu)在內(nèi)核生成一個(gè)VMA后,它會(huì)調(diào)用該VMA的open()函數(shù),例如fork一個(gè)繼承父繼承資源的子進(jìn)程時(shí)。但是,當(dāng)用戶進(jìn)行mmap()系統(tǒng)調(diào)用后,盡管VMA在設(shè)備驅(qū)動(dòng)文件操作結(jié)構(gòu)體mmap()被調(diào)用前就已產(chǎn)生,內(nèi)核卻不會(huì)調(diào)用VMA的open()函數(shù),通常需要在驅(qū)動(dòng)的mmap()數(shù)中顯示調(diào)用vma->vm_ops->open()。代范例11.7給出了一個(gè)vm_operations_struct的操第章structvm_operations_structvoid(*open)(structvm_area_struct*area);/*VMA的函數(shù)void(*close)(structvm_area_struct*area);/*VMA的函數(shù)structpage*(*nopage)(structvm_area_struct*area,unsignedlong*type); 的頁不在內(nèi)存時(shí)調(diào)用(*populate)(structvm_area_struct*area,unsignedlongaddress,longlen,pgprot_tprot,unsignedlongpgoff, 9structvm_area_structstructmm_struct*vm_mm;*所處的地址空unsignedlongvm_start;*開始虛擬地unsignedlongvm_end;*結(jié)束虛擬地*/pgprot_tvm_page_prot; unsignedlongvm_flags;/*標(biāo)志,VM_READ/VM_WRITE/VM_EXEC/VM_SHARED等*/ /*VMA的函數(shù)集指structvm_operations_struct*vm_ops;unsignedlongvm_pgoff;*偏移(頁幀號)structfilevoid 16Linux#4%解(第2版vm_operations_struct-ǘ3?μ?vma-startLinux#4%解(第2版vm_operations_struct-ǘ3?μ?vma-start?vma-e?addr??????1oremap_pfn_range()q?!addrpfn{???1o??ù???ê???1o

ǐPAGE_SHIFT?PAGE_SIZE!4KBvPAGE_SHIFT!12′!PAGE_SIZEǘ,prot?L?h??Q???X86???1MB??±??I/Oh???{?īǒ??9??reserve()??!Q?|ɑ?11.8kmalloc()AQC__initkmalloc_map_init(void)3{ remap_pfn_range(structvm_area_struct*vma,unsignedlongaddr,unsignedlongpfn,unsignedlongsize,pgprot_tprot);1s _mmap(structfile*filp,structvm_area_struct*vma)2{if(remap_pfn_range(vma,vma->vm_start,vm->vm_pgoff,vma->vm_end-->vm_start,vma->vm_page_prot))/*?ǔ??return-vma->vm_ops= return911void _vma_open(structvm_area_struct*vma) /*VMA??q?*/12{ k(KERN_NOTICE VMAopen,virt%lx,phys%lx\n",vma-vma->vm_pgoff<<PAGE_SHIFT);16}18 _vma_close(structvm_area_struct //VMA19 k(KERN_NOTICE" VMAclose.\n");22} icstruct _remap_vm_ops={/*VMA?J??H.open .close= 28內(nèi)存與I/Oǘ28ǘ4??SHARED???_PAGE_PRESENT|_PAGE_USER|內(nèi)存與I/Oǘ28ǘ4??SHARED???_PAGE_PRESENT|_PAGE_USER|_PAGE_RW?úI/Onocache????nocache? ??avma->vm_page_prot11.9 nocache2#"8EW?7?ǘ3??pgprot_noncached()?ù???O?,CPUH???ARM??pgprot_noncached()ǒ?èbine()第章#definepgprot_noncached(prot)pgprot(pgprot_val(prot)&~(L_PTE_CACHEABLE|L_PTE_1s _nocache_mmap(structfile*filp,structvm_area_struct*vma)2{vma->vm_page_prot=pgprot_noncached(vma->vm_page_prot);/*?nocache??vma->vm_pgoff=((u32)map_start>>PAGE_SHIFT); if(remap_pfn_range(vma,vma->vm_start,vma->vm_pgoff,vma->vm_end-->vm_start,vma-return-return10/*???????cdev??Hbuffer=kmalloc(BUF_SIZE,GFP_KERNEL);//??bufferfor(page=virt_to_page(buffer);page<virt_to_page(buffer+memmapreserve(page);/*??!Q?*/11} kmalloc_map_mmap(structfile*filp,structvm_area_struct*vma)14{unsignedlong unsignedlongstart=(unsignedlong)vma->vmunsignedlongsize=(unsignedlong)(vma->vm_end-vma- k(KERN_INFO"mmaptest_mmap/* ???êéif(size>return-EINVAL; =(unsigned buffer??ū?while(size>0) /* ?*/page= if(remappagerange(start,page,PAGESIZE,PAGEreturn-start+= +=size-=}return0;34}Linux#4%解(第2版)?d??Cache±j?kwritej?kARM?j?k2??ú??2F,??2?Linux#4%解(第2版)?d??Cache±j?kwritej?kARM?j?k2??ú??2F,??2?e???,???2?±Cache5???"?j?J??p?j?k?Cache?,¤??2nopage()?)remap_pfn_range()9????ǐā??VMA??????h???????ú????′!??????ú???A???1?{?????1o???2????r?3?????a????????v???VMA?nopage()????3???4??nopage()|?īǒ??9?? 11.10?p)????L?????ú?1onopage()0?3???ǖ????nopage()remap_pfn_range()??é?z?,remap_pfn_range()?nopage()???,1s _mmap(structfile*filp,structvm_area_struct*vma)2{unsignedlongoffset=vma->vm_pgoff<<if(offset>=__pa(high_memory)||(filp->f_flagsvma-vma- vma-return10structpage _vma_nopage(structvm_area_struct*vma,unsigned 14{structpageunsignedlongoffset=vma->vm_pgoff<<unsignedlongphysaddr=address-vma->vm_start+offset;/*??1ounsignedlongpageframe=physaddr>>PAGE_SHIFT;/*?ù?if /*?ù?ū?returnpageptr=pfn_to_page(pageframe);/*?ù?->???ǖ /*???????L???if*type=returnpageptr; /*?3???ǖ*/26} pgprot(pgprot_val(prot)&內(nèi)存與I/O幎大多數(shù)幍?洀┷都不需要提供幍?內(nèi)存到用?空梃能力,因?,?于串口等面流的幍?而言,??扨毫無意?。而?于?示、展欠等內(nèi)存與I/O幎大多數(shù)幍?洀┷都不需要提供幍?內(nèi)存到用?空梃能力,因?,?于串口等面流的幍?而言,??扨毫無意?。而?于?示、展欠等幍?,建可?少用?梃和內(nèi)核空梃之梃的內(nèi)存拷徬I/O內(nèi)存靜日在將Linux移植到目標(biāo)電路板的過程中,通常會(huì)建立外設(shè)I/O內(nèi)存物理地址到虛擬地址的態(tài),這通過在電路板對應(yīng)的map_desc結(jié)構(gòu)體數(shù)組中添加新的成員來完成結(jié)構(gòu)體的定義如代11.11所示代 map_desc結(jié)構(gòu)例如,在內(nèi)核arch/arm/mach-ixp2000/ixdp2x01.c文件對應(yīng)elIXDP2401IXDP2801臺上包含一個(gè)CPLD,該文件中就進(jìn)行了CPLD物理地址到虛擬地址的靜 如代 所示射123456789sic==_==s{icvoid__initiotable_init(&ixdp2x01_io_desc,1);12代11.12中的11iotable_init()是最終建立的函數(shù),它被通過START、MACHINE_END宏賦值給板的map_io()函數(shù)。Linux操作系統(tǒng)移植到特定上,MACHINE_START、MACHINE_END宏之間的定義針對特定電路板而設(shè)計(jì),其中的map_io()員函數(shù)完成I/O內(nèi)存的靜 ,代MACHINE_END宏的例子11.13給出IXDP2401電路板MACHINE_START代 IXDP2401電路板的MACHINE_START、MACHINE_END第章MACHINE_START(IXDP2401, elIXDP2401Development/* ainer:MontaVistaSoftware,Inc. =structmapdescunsignedlongvirtual;/*虛擬地unsignedlongpfn;/*phys_to_pfn(phy_addr)unsignedlonglength;* type;/**/6};Linux#4%解(第2版???ǐ??OS?h???óǐ÷?c?9a?ú?h????I/OLinux#4%解(第2版???ǐ??OS?h???óǐ÷?c?9a?ú?h????I/Oh??|2??2 ??????|2??2ǘO???????L??m??{desc??11.14?Mò?p)h?ǒ????L??m???rmap_desc9+BS5R='1*??1o* 40000000PCI PHYS_PCI_MEM_BASE(max* 61000000PCI PHYS_PCI_CONFIG_BASE(max* 62000000PCIV3 PHYS_PCI_V3_BASE(max* 60000000PCI PHYS_PCI_IO_BASE(max*ef000000 Cacheflush *f1000000 *f1100000 11000000???|??210*f1200000 12000000EBI??211* 1300000012* 1400000013* 16000000UART14* 17000000UART15*16* 1719s icstructmap_descap_io_desc[] initdata={20{.virtual= = =MT_DEVICE25},{.virtual= = =MT_DEVICE30},{.virtual= = =MT_DEVICE35},{.virtual= = =MT_DEVICE40},{ =((IXP2000_UART_VIRT_BASE)>>18)&0x =0x00000100, = = =.init =ixdp2x01init內(nèi)存與I/O幎此后,在設(shè)備驅(qū)動(dòng)map_desc數(shù)I/O內(nèi)存時(shí),直接內(nèi)存與I/O幎此后,在設(shè)備驅(qū)動(dòng)map_desc數(shù)I/O內(nèi)存時(shí),直接map_desc中該的虛擬地址上加上相應(yīng)的偏移即可,不再需要使用ioremap()若要在LDD6410開發(fā)板的板文件中添加新的物理地址到虛擬地址,只需要修文件/arch/arm/mach-s3c6410/mach-ldd6410.c中的map_desc數(shù)組(目前該數(shù)組為空 structmap_descldd6410_iodesc[]= 第章.virtual= = = =MT_DEVICE45},{.virtual= = = =MT_DEVICE50},{.virtual= = = =MT_DEVICE55},{.virtual= = = =MT_DEVICE60},{.virtual= = = =MT_DEVICE65},{.virtual= = =}, = = =MT_DEVICE75},{.virtual= = = =MT_DEVICE80},{.virtual= = = =MT_DEVICE85}86Linux#4%解(第2版DMA?ǎ??CPU??ê?9?????h?&?ɑ??¨??B???;?|L?DMA?9L??CPU5???I/O??B??ǐň?p?5?éé??????§?Linux#4%解(第2版DMA?ǎ??CPU??ê?9?????h?&?ɑ??¨??B???;?|L?DMA?9L??CPU5???I/O??B??ǐň?p?5?éé??????§?DMA?ú?;H????z????????às?dDMA?????B??DMA?|2DMAC?|?B???CPUe6<??DMA??|DMAC?????CPU??B??????|?CPU????????ǐāɑ?|??DMACache一致Cache±DMA??D'???d?*?Cache??ZCPU?ah?????5??aCPU???ú??h?/-??DMA?9?Zh???&?B??????B???&???????CPUX?DMA?ah????1oCache??·11.10??±Cache&???×?*E???DMA???1oCacheū?·11.11????DMA?JCache??a??h???????S??CPU?ɡ?4??!Cache???ê?h?????h??L????11.10DMA目的地址Cache象不交DMA目的地址Cache象交Cache(??,"?Cache"?????vfū??????vfū?????z????Cacheh???????ú?x???????Cacheh?????|??????ú?????ū?d??????l?,DMA???CacheDMA?Cache??????????E?????·11.12??!Cache±DMA內(nèi)存與I/O幎易用兩個(gè)方面時(shí)處于蹺蹺板兩端。內(nèi)存與I/O幎易用兩個(gè)方面時(shí)處于蹺蹺板兩端。Cache的不一致桽歐并非只是?生DMA的情況下,?棔上,執(zhí)存在Cache使能和?桼的?刻。例如,?于サMMU功能的ARM?理器,在??MMU之前,需要先置Cache無效,TLB也是如此,代?清◤11.15所全出的一段?冥被用來完成此任┰。代 置ARM的Cache無DMA本身不屬于一種等同于字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備的外設(shè),它只是外設(shè)與內(nèi)存交數(shù)據(jù)式。因此,本節(jié)的標(biāo)題不是“LinuxDMA驅(qū)動(dòng)”而是“LinuxDMA編程內(nèi)存中用于與外設(shè)交互數(shù)據(jù)的一塊區(qū)域被稱做DMA緩沖區(qū),在設(shè)備不支持scatter/gather(分散/,簡SG)操作的情況下,DMA緩沖區(qū)必須是物理上連續(xù)的。1.DMAX86系統(tǒng)ISA設(shè)備而言DMA操作只能16MB以下的內(nèi)存中進(jìn)行,因此,在使保證獲得的內(nèi)存位DMA_ZONE,是具DMA能力的。內(nèi)核中定義了get_free_pages()DMA的“快捷方式”__get_dma_pages(),它在申請標(biāo)中添加了GFP_DMA,如下所示如果不想使用log2size即order為參數(shù)申請DMA內(nèi)存,則可以使用另一個(gè)函數(shù)alloc(),其源代碼如代11.16所示代 dma_mem_alloc()函第章1s icunsignedlongdma_mem_alloc( 2{#define__get_dma_pages(gfp_mask,order)get_free_pages((gfp_mask)|1/*使cache無效*/2"mov r0,#0\n"3 p15,0,r0,c7,c7,0\n"/*使數(shù)據(jù)和指cache4 p15,0,r0,c7,c10,4\n"/*放空寫緩5 p15,0,r0,c8,c7,0\n"/*TLBLinux#4%解(第2版??2??DMA?J?9??ú?h???ɑ?′?ZONE2N*>??,DMA??;L???1o????1o??1o?5??????1ov?5CPUMMU???{?h?1o5CPUoLinux#4%解(第2版??2??DMA?J?9??ú?h???ɑ?′?ZONE2N*>??,DMA??;L???1o????1o??1o?5??????1ov?5CPUMMU???{?h?1o5CPUo???PCa,ISA±PCI????1o?!??1o′!ū?U?????????????????A?I/O!¤???1o?CReferencePlatform????1o0???ǖ????0x80000000?!??1o0xC0000000?9¤1oêf?)??1o00x80000000???1o0xC0000000?ū?|!???????1oh??N)??L?IOMMUī???k???m?q??A?úóJ??q????IOMMU??m?S???2|?9L?SG11.13MMU3DMA???ú???ū?h?1o??DMA?J??ǎ?m????DMA dma_set_mask(structdevice*dev,u64 M?a,???24F1o??DMA?J?????ê????dma_set_mask(dev, 4L%DMA?????óJr??DMA?k?o¤?(????Cache????h??N)9q??,r?unsignedlongvirt_to_bus(volatilevoid*address);void*bus_to_virt(unsignedlongaddress);order=get_order(size);/*é?>??return__get_dma_pages(GFP_KERNEL,order);5}內(nèi)存與I/O?q???3W!??{?DMA?k????1o???q??????handle3DMA?k????1ohandle?ǜ?!dma_addr_tdma_alloc_coherent()???DMA?k???k??Cache9q??,r?內(nèi)存與I/O?q???3W!??{?DMA?k????1o???q??????handle3DMA?k????1ohandle?ǜ?!dma_addr_tdma_alloc_coherent()???DMA?k???k??Cache9q??,r?bining?DMAbine()??ê?coherent() q?!pci_free_consistent()5;#DMA???ū?DMA?k???????????????????DMA?k???ū?P????)Cache????E????m?k???h?????????????????jb?????ǘ?ē????????get_free_pages()ǘ??????Uê?L?????DMA?k?L??12??DMA3ɑ???DMA???J?????ê?ɑ?Cache?invalidateīflush?J9

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論