kmalloc

kmalloc計算機語言的一種函式名,分配記憶體。語法,void *kmalloc(size_t size, int flags);size要分配記憶體的大小. 以位元組為單位.flags要分配記憶體的類型。在設備驅動程式或者核心模組中動態開闢記憶體,不是用malloc,而是kmalloc ,vmalloc,或者用get_free_pages直接申請頁。釋放記憶體用的是kfree,vfree,或free_pages. kmalloc函式返回的是虛擬地址(線性地址). kmalloc特殊之處在於它分配的記憶體是物理上連續的,這對於要進行DMA的設備十分重要. 而用vmalloc分配的記憶體只是線性地址連續,物理地址不一定連續,不能直接用於DMA。

基本信息

kmalloc計算機語言的一種函式名,分配記憶體。語法,void * kmalloc(size_t size, int flags); size要分配記憶體的大小. 以位元組為單位. flags要分配記憶體的類型。在設備驅動程式或者核心模組中動態開闢記憶體,不是用malloc,而是kmalloc ,vmalloc,或者用get_free_pages直接申請頁。釋放記憶體用的是kfree,vfree,或free_pages. kmalloc函式返回的是虛擬地址(線性地址). kmalloc特殊之處在於它分配的記憶體是物理上連續的,這對於要進行DMA的設備十分重要. 而用vmalloc分配的記憶體只是線性地址連續,物理地址不一定連續,不能直接用於DMA。

kmalloc最大只能開闢128k-16,16個位元組是被頁描述符結構占用了。記憶體映射的I/O口,暫存器或者是硬體設備的RAM(如顯存)一般占用F0000000以上的地址空間。在驅動程式中不能直接訪問,要通過kernel函式vremap獲得重新映射以後的地址。另外,很多硬體需要一塊比較大的連續記憶體用作DMA傳送。這塊記憶體需要一直駐留在記憶體,不能被交換到檔案中去。但是kmalloc最多只能開闢大小為32XPAGE_SIZE的記憶體,一般的PAGE_SIZE=4kB,也就是128kB的大小的記憶體。

記憶體管理

對於提供了MMU(存儲管理器,輔助作業系統進行記憶體管理,提供虛實地址轉換等硬體支持)的處理器而言,Linux提供了複雜的存儲管理系統,使得進程所能訪問的記憶體達到4GB。進程的4GB記憶體空間被人為的分為兩個部分--用戶空間與核心空間。用戶空間地址分布從0到3GB(PAGE_OFFSET,在0x86中它等於0xC0000000),3GB到4GB為核心空間。核心空間中,從3G到vmalloc_start這段地址是物理記憶體映射區域(該區域中包含了核心鏡像、物理頁框表mem_map等等),比如我們使用的 VMware虛擬系統記憶體是160M,那么3G~3G+160M這片記憶體就應該映射物理記憶體。在物理記憶體映射區之後,就是vmalloc區域。對於 160M的系統而言,vmalloc_start位置應在3G+160M附近(在物理記憶體映射區與vmalloc_start期間還存在一個8M的gap 來防止越界),vmalloc_end的位置接近4G(最後位置系統會保留一片128k大小的區域用於專用頁面映射)

函式分析

功能

kmalloc() 分配連續的物理地址,用於小記憶體分配。get_free_page() 分配連續的物理地址,用於整頁分配。kmalloc() 函式本身是基於 slab 實現的。slab 是為分配小記憶體提供的一種高效機制。但 slab 這種分配機制又不是獨立的,它本身也是在頁分配器的基礎上來劃分更細粒度的記憶體供調用者使用。也就是說系統先用頁分配器分配以頁為最小單位的連續物理地址,然後 kmalloc() 再在這上面根據調用者的需要進行切分。 kmalloc() 的實現,kmalloc()函式的實現是在 __do_kmalloc() 中,可以看到在 __do_kmalloc()代碼里最終調用了 __cache_alloc() 來分配一個 slab,其實kmem_cache_alloc() 等函式的實現也是調用了這個函式來分配新的 slab。我們按照 __cache_alloc()函式的調用路徑一直跟蹤下去會發 cache_grow() 函式中使用了 kmem_getpages()函式來分配一個物理頁面,kmem_getpages() 函式中調用的alloc_pages_node() 最終是使用 __alloc_pages() 來返回一個struct page 結構,而這個結構正是系統用來描述物理頁面的。這樣也就證實了上面所說的,slab 是在物理頁面基礎上實現的。kmalloc() 分配的是物理地址。

物理記憶體

__get_free_page() 是頁面分配器提供給調用者的最底層的記憶體分配函式。它分配連續的物理記憶體。__get_free_page() 函式本身是基於 buddy 實現的。在使用 buddy 實現的物理記憶體管理中最小分配粒度是以頁為單位的。這個函式是如何分配到物理頁面又是在什麼區域中進行分配的?在 __alloc_pages() 函式中,多次嘗試調用get_page_from_freelist() 函式從 zonelist 中取得相關 zone,並從其中返回一個可用的 struct page 頁面,一個物理頁面的分配是從 zonelist(一個 zone 的結構數組)中的 zone 返回的。以上分析已經明確了__get_free_page() 函式分配物理記憶體的流程。

結構

kmalloc和get_free_page申請的記憶體位於物理記憶體映射區域,而且在物理上也是連續的,它們與真實的物理地址只有一個固定的偏移,因此存在較簡單的轉換關係,virt_to_phys()可以實現核心虛擬地址轉化為物理地址:

上面轉換過程是將虛擬地址減去3G(PAGE_OFFSET=0XC000000)。與之對應的函式為phys_to_virt(),將核心物理地址轉化為虛擬地址:

virt_to_phys()和phys_to_virt()都定義在include\asm-i386\io.h中。

運行一次上述程式,發現pagemem的地址在0xc7997000(約3G+121M)、kmallocmem 地址在0xc9bc1380(約3G+155M)、vmallocmem的地址在0xcabeb000(約3G+171M)處,符合前文所述的記憶體布局。

相關詞條

相關搜尋

熱門詞條

聯絡我們