http://blog.chinaunix.net/uid-9185047-id-445143.html
2010
make menuconfig后,生成新的配置.config。make時候,把新配置文件轉(zhuǎn)化為autoconf.h,使用時候一般應用為include/linux/autoconf.h->mmdebug.h->mm.h??梢灾苯右?。
kconfig文件為menu的配置選項文件,功能設置比較簡單,如下:
config DMA_MEM
config DMA_MEM_MAX_COUNT
config DMA_MEM_RESERVER_COUNT
#define CONFIG_DMA_MEM
#define CONFIG_DMA_MEM_MAX_COUNT 64
#define CONFIG_DMA_MEM_RESERVER_COUNT 24
linux內(nèi)核內(nèi)部提供3個刷新cache函數(shù),使用前需要打開CONFIG_DMA_NONCOHERENT宏,這個宏內(nèi)嵌在各個芯片默認的config文件中,如linux-2.6.27.28\arch\mips\configs\malta_defconfig
#define dma_cache_wback_inv(start, size)
#define dma_cache_wback(start, size)
#define dma_cache_inv(start, size)
對外只通過_sys_sysmips帶入FLUSH_CACHE參數(shù),調(diào)用__flush_cache_all函數(shù)。如有需要可在syscall.c中增加新的用戶態(tài)接口
系統(tǒng)共享內(nèi)存的2種方式:
shm_open和open類似,通過一個文件(類似驅(qū)動)作為內(nèi)存共享的中介(mmap)。所以系統(tǒng)必須要有/dev/shm才能使用shm_open。shm_open是posix的實現(xiàn),可以實現(xiàn)無親緣關系的進程共享內(nèi)存。使用上較為常見。Shm_open的第一個參數(shù),默認為在/dev/shm/下的文件,如:”mytemp”=/dev/shm/mytemp,Man幫助文檔說明中指出,shm_open的名字必須以/開頭,但是中間不能再有/
shmget是system實現(xiàn),不依賴其他驅(qū)動。可以為無親緣、親緣進程共享,其特點是需要key。這個key生產(chǎn)可以通過ftok查找一個真實存在文件,通過此路徑及進程內(nèi)共享內(nèi)存id號,生產(chǎn)特定的key值。也可以是親緣進程通過IPC_PRIVATE共享(和shm_open不一樣的是,這個文件并不是實現(xiàn)的中介,只是使用文件的節(jié)點信息)。Shmget使用上沒有shm_open常見,但是更普遍的存在各個版本linux中(如各種BSD,只能使用shmget)
ftok原型如下:
key_t ftok( char * fname, int id )fname就時你指定的文件名,id是子序號。
在一般的UNIX實現(xiàn)中,是將文件的索引節(jié)點號取出,前面加上子序號得到key_t的返回值。
如指定文件的索引節(jié)點號為65538,換算成16進制為0x010002,而你指定的ID值為38,換算成16進制為0x26,則最后的key_t返回值為0x26010002。
查詢文件索引節(jié)點號的方法是: ls -i
當刪除重建文件后,索引節(jié)點號由操作系統(tǒng)根據(jù)當時文件系統(tǒng)的使用情況分配,因此與原來不同,所以得到的索引節(jié)點號也不同。如果要確保key_t值不變,要目確保ftok的文件不被刪除,要么不用ftok,指定一個固定的key_t值。
shmget()是用來開辟/指向一塊共享內(nèi)存的函數(shù)。參數(shù)定義如下:
key_t shmkey 是這塊共享內(nèi)存的標識符。如果是父子關系的進程間通信的話,這個標識符用IPC_PRIVATE來代替。如兩個進程沒有任何關系,就用ftok()算出來一個標識符使用。
1、內(nèi)核提供了remap_page_range函數(shù),可以實現(xiàn)物理、內(nèi)核高端地址,映射到用戶態(tài)空間。但需要注意的是,對于內(nèi)存而言,2.4內(nèi)核必須設置PG_Reserved標記,防止內(nèi)存被交換出去。因此2.4版本內(nèi)核做這樣的工作是比較繁瑣的,需要一頁頁的做。到了2.6時代,PG_Reserved已經(jīng)放棄了,而是使用vm_reserved標記,這是針對一片區(qū)域的屬性,所以目前的實現(xiàn)可以比較簡單。除了通過remap_pfn_range函數(shù)外,還可以使用零頁方式實現(xiàn)。零頁:延后建表功能。不過實現(xiàn)較為復雜。
2、內(nèi)核提供了/dev/mem的設備,已經(jīng)實現(xiàn)了部分功能。這里指的部分功能是有限的、不完整的。VM對于內(nèi)存和寄存器應該有不同的區(qū)分。因此VM結構體有對應的VM_IO做區(qū)分。remap_page_range需要判斷查看所請求的偏移量(保存在vma->vm_pgoff中)是否超出了物理內(nèi)存。如果是,則設置VMA的VM_IO標志,以標志該區(qū)域為I/O內(nèi)存。非內(nèi)存的寄存器空間要標示為IO空間,防止core dump。(core dump:如程序運行時當?shù)簦@時操作系統(tǒng)就會把程序當?shù)?時的內(nèi)存內(nèi)容 dump 出來,做為debugg參考。這個動作就叫作 core dump)
LDD2上提到,通過kmalloc和__get_free_pages得到的物理地址,不應該通過mmap進行用戶態(tài)訪問。原因是/dev/mem設備的實現(xiàn)中,并沒有做VM_RESERVED和VM_IO的標記。因此對于內(nèi)存對象而言,并不保證不置換出去(普通讀寫應無問題,DMA問題可能比較大)。對于IO地址而言,又不保證不做core dump。而此要利用mem設備,必須進行一定的修改。目前所見的顯卡驅(qū)動中(與需求很類似),它們代碼中都明確的寫入此2標記,用作顯存和顯卡寄存器的訪問(因均不為物理內(nèi)存地址之內(nèi))。
PS:mem設備應該在bootmem_init函數(shù),通過boot_mem_map數(shù)組來指定可訪問的各個物理地址空間。對于mips而言是4G大小,而其他芯片有不同的限制。實際直接使用當前mips的編寫也不完全正確。因為我們的芯片確實還有許多未定義的物理地址空間,應對此類地址加以檢查,或需要上層使用時額外注意,否則可能會導致使用的exception。
3、kmalloc最終通過__get_free_pages函數(shù)獲取物理連續(xù)頁,但據(jù)說是kmalloc是不產(chǎn)生syscall的,而且申請大小限制在128K。
Kmalloc包含了許多工作,其中有分中斷、非中斷模式,還有smp相關的內(nèi)容。標記解釋如下:
情形
進程上下文,可以睡眠
進程上下文,不可以睡眠
中斷處理程序
軟中斷
tasklet
需要用于DMA的內(nèi)存,可以調(diào)度
需要用于DMA的內(nèi)存,不可以調(diào)度
4、 kmalloc根據(jù)MAX_ORDER定義,決定一次最多能分配的內(nèi)存大小。修改MAX_ORDER,保證大塊內(nèi)存的申請
能利用原有的slab分配是比較穩(wěn)定、快速的方式。其中GFP_DMA指明內(nèi)存從保留的DMA內(nèi)存區(qū)域中分配,這就很符和當前的需求。kmem_cache_init初始化系統(tǒng)內(nèi)存時,根據(jù)ZONE_DMA的位置,分配給cs_dmacachep。而其他內(nèi)存則標記在ZONE_NORMAL,分配給cs_cachep。這就能實現(xiàn)系統(tǒng)申請普通內(nèi)存和DMA內(nèi)存的區(qū)分。
綜上所述,目前的策略定為:
1、
2、
3、
4、
動態(tài)庫暫提供4個接口,dma_malloc、dma_free、mmap_iospace、munmap_iospace。
1、
2、
3、
4、
再仔細查找了一下內(nèi)核代碼,發(fā)現(xiàn)不少驅(qū)動默認帶入GFP_DMA標記。當系統(tǒng)有ZONE_DMA,則從DMA區(qū)域分配。沒有則從ZONE_NORMAL分配。因此想利用ZONE_DMA需要排除其他驅(qū)動不再使用此標記,這樣的可移植性較差。
因而改為保留區(qū)域是系統(tǒng)看不見的區(qū)域,使用mem設備在用戶態(tài)mmap映射出3段虛擬地址空間,分別對應write though/write bak/uncache。這三段區(qū)域其實都是映射到同一段物理地址上,那么管理模塊就可以根據(jù)需要,利用地址偏移就可以快速給出申請的虛擬、物理地址。
由于內(nèi)核只提供了phys_mem_access_prot函數(shù),做uncache內(nèi)存的建表功能,所以要新增2個函數(shù)。參照pgprot_noncached實現(xiàn),其實也很簡單,只是把對應芯片的tag標記加上即可,如:prot = (prot & ~_CACHE_MASK) | _CACHE_WRITEBACK;。當然_CACHE_WRITEBACK沒有定義的,查找一下芯片手冊就能得出。
內(nèi)存管理使用bitmap映射方式,內(nèi)核提供了一套查找方法find_next_bit和find_next_zero_bit。對應大段、小端機器進行了算法改進。同時如果芯片支持bit檢測功能,內(nèi)核也加入對應的實現(xiàn)了。
值得一提的是,內(nèi)核還提供了海明距離的優(yōu)化函數(shù):hweight32/hweight16/hweight8。能快速得出一個字段內(nèi)有多少個bit是1。
在信息論中,兩個等長字符串之間的漢明距離是兩個字符串對應位置的不同字符的個數(shù)。換句話說,它就是將一個字符串變換成另外一個字符串所需要替換的字符個數(shù)。 例如:
1011101 與 1001001 之間的漢明距離是 2。
2143896 與 2233796 之間的漢明距離是 3。
"toned" 與 "roses" 之間的漢明距離是 3。
漢明重量是字符串相對于同樣長度的零字符串的漢明距離,也就是說,它是字符串中非零的元素個數(shù):對于二進制字符串來說,就是 1 的個數(shù),所以 11101 的漢明重量是 4。