在一般情況下,Linux在初始化時(shí),總是盡可能的將所有的物理內(nèi)存映射到內(nèi)核地址空間中去。如果內(nèi)核地址空間起始于0xC0000000,為vmalloc保留的虛擬地址空間是128M,那么最多只能有(1G-128M)的物理內(nèi)存直接映射到內(nèi)核空間中,內(nèi)核可以直接訪問(wèn)。如果還有更多的物理內(nèi)存,就稱為高端內(nèi)存,內(nèi)核不能直接訪問(wèn),只能通過(guò)修改頁(yè)表映射后才能進(jìn)行訪問(wèn)。
內(nèi)存分區(qū)可以使內(nèi)核頁(yè)分配更加合理。當(dāng)系統(tǒng)物理內(nèi)存大于1G時(shí),內(nèi)核不能將所有的物理內(nèi)存都預(yù)先映射到內(nèi)核空間中,這樣就產(chǎn)生了高端內(nèi)存,高端內(nèi)存最適于映射到用戶進(jìn)程空間中。預(yù)映射的部分可直接用于內(nèi)核緩沖區(qū),其中有一小塊可用于DMA操作的內(nèi)存,留給DMA操作分配用,一般不會(huì)輕易分配。內(nèi)存分區(qū)還可以適應(yīng)不連續(xù)的物理內(nèi)存分布,是非一致性內(nèi)存存取體系(NUMA)的基礎(chǔ)。
在 32 位機(jī)器上頁(yè)表通常只可以存儲(chǔ)在低端內(nèi)存中。低端內(nèi)存只限于物理內(nèi)存的前 896 MB,同時(shí)還要滿足內(nèi)核其余的大部分要求。在應(yīng)用程序使用了大量進(jìn)程并映射了大量?jī)?nèi)存的情況下,低端內(nèi)存可能很快就不夠用了。在 2.6 內(nèi)核中有一個(gè)配置選項(xiàng)叫做Highmem PTE,讓頁(yè)表?xiàng)l目可以存放在高端內(nèi)存中,釋放出更多的低端內(nèi)存區(qū)域給那些必須放在這里的其他內(nèi)核數(shù)據(jù)結(jié)構(gòu)。作為代價(jià),使用這些頁(yè)表?xiàng)l目的進(jìn)程會(huì)稍微慢一些。不過(guò),對(duì)于那些在大量進(jìn)程在運(yùn)行的系統(tǒng)來(lái)說(shuō),將頁(yè)表存儲(chǔ)到高端內(nèi)存中可以在低端內(nèi)存區(qū)域擠出更多的內(nèi)存。
在申請(qǐng)和釋放較小且連續(xù)的內(nèi)存空間時(shí),使用kmalloc()和kfree()在物理內(nèi)存中進(jìn)行分配, 申請(qǐng)較大的內(nèi)存空間時(shí),使用vmalloc()。由vmalloc()申請(qǐng)的內(nèi)存空間在虛擬內(nèi)存中是連續(xù)的,它們映射到在物理內(nèi)存時(shí),可以使用不連續(xù)的物理頁(yè)面,而且僅把當(dāng)前訪問(wèn)的部分放在物理頁(yè)面中。本節(jié)要介紹的內(nèi)存分配函數(shù)是vmalloc。盡管這段區(qū)域在物理上可能是不連續(xù)的(要訪問(wèn)其中的每個(gè)頁(yè)面都必須獨(dú)立地調(diào)用函數(shù)__get_free_page),內(nèi)核卻認(rèn)為它們?cè)诘刂飞鲜沁B續(xù)的。分配的內(nèi)存空間被映射進(jìn)入內(nèi)核數(shù)據(jù)段中,從用戶空間是不可見(jiàn)的-這一點(diǎn)上與其他分配技術(shù)不同。vmalloc發(fā)生錯(cuò)誤時(shí)返回0(NULL地址),成功時(shí)返回一個(gè)指向一個(gè)大小為size的線性地址空間的指針。vmalloc函數(shù)在核心中所分配內(nèi)存有vm_struct結(jié)構(gòu)的鏈表所支持,如圖所示。
使用vmalloc時(shí)應(yīng)將<linux/vmalloc.h>包含進(jìn)來(lái)。與其他內(nèi)存分配函數(shù)不同的是,vmalloc返回很“高”的地址值-這些地址要高于物理內(nèi)存的頂部。由于vmalloc對(duì)頁(yè)表調(diào)整后允許用連續(xù)的“高”地址訪問(wèn)分配得到的頁(yè)面,因此處理器是可以訪問(wèn)返回得到的內(nèi)存區(qū)域的。內(nèi)核能和其他地址一樣地使用vmalloc返回的地址,但程序中用到的這個(gè)地址與地址總線上的地址并不相同。
用vmalloc分配得到的地址是不能在微處理器之外使用的,因?yàn)樗鼈冎挥性谔幚砥鞯姆猪?yè)單元之上才有意義。但驅(qū)動(dòng)程序需要真正的物理地址時(shí)(象外設(shè)用以驅(qū)動(dòng)系統(tǒng)總線的DMA地址),這樣的地址是不能通過(guò)vmalloc函數(shù)分配的。正確使用vmalloc函數(shù)的場(chǎng)合是為軟件分配一大塊連續(xù)的用于緩沖的內(nèi)存區(qū)域。注意vmalloc的開(kāi)銷要比__get_free_pages大,因?yàn)樗幚慝@取內(nèi)存還要建立頁(yè)表。因此,不值得用vmalloc函數(shù)只分配一頁(yè)的內(nèi)存空間。
vmalloc分配的內(nèi)核虛擬內(nèi)存與kmalloc/__get_free_pages分配的內(nèi)核邏輯內(nèi)存位于不同的區(qū)間,不會(huì)重疊。因?yàn)閮?nèi)核空間被分區(qū)管理,各司其職。用戶空間被分配在0-3G,3G之后緊跟著是物理內(nèi)存映射區(qū)間,再后面才是vmalloc_start開(kāi)始的用于vmalloc分配內(nèi)存的地址空間。
ioremap用于將高內(nèi)存空間的PCI緩沖區(qū)映射到用戶空間。例如,如果VGA設(shè)備的幀緩沖區(qū)被映射到地址0xf0000000(典型的一個(gè)值)后,ioremap就可以建立正確的頁(yè)表讓處理機(jī)可以訪問(wèn)。而系統(tǒng)初始化時(shí)建立的頁(yè)表只是用于訪問(wèn)低于物理地址空間的內(nèi)存區(qū)域。系統(tǒng)的初始化過(guò)程并不檢測(cè)PCI緩沖區(qū),而是由各個(gè)驅(qū)動(dòng)程序自己負(fù)責(zé)管理自己的緩沖區(qū)。
如果希望驅(qū)動(dòng)程序能在不同的平臺(tái)間移植,那么使用ioremap時(shí)就要小心。在一些平臺(tái)上是不能直接將PCI內(nèi)存區(qū)域映射到處理機(jī)的地址空間的,例如Alpha上就不行。此時(shí)你就不能象普通內(nèi)存區(qū)域那樣地對(duì)重映射區(qū)域進(jìn)行訪問(wèn),你要用readb函數(shù)或者其他一些I/O函數(shù)。這套函數(shù)可以在不同平臺(tái)間移植。
對(duì)vmalloc和ioremap函數(shù)可分配的內(nèi)存空間大小并沒(méi)有什么限制,但為了能檢測(cè)到程序員的犯下的一些錯(cuò)誤,vmalloc不允許分配超過(guò)物理內(nèi)存大小的內(nèi)存空間。但是記著,vmalloc函數(shù)請(qǐng)求過(guò)多的內(nèi)存空間會(huì)產(chǎn)生一些和調(diào)用kmalloc函數(shù)時(shí)相同的問(wèn)題。
ioremap和vmalloc函數(shù)都是面向頁(yè)的(它們都會(huì)修改頁(yè)表),因此分配或釋放的內(nèi)存空間實(shí)際上都會(huì)上調(diào)為最近的一個(gè)頁(yè)邊界。而且,ioremap函數(shù)并不考慮如何重映射不是頁(yè)邊界的物理地址。
這樣對(duì)內(nèi)存的知識(shí)大家應(yīng)該全明白了吧,謝謝大家。
聯(lián)系客服