複製鏈接
請複製以下鏈接發送給好友

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
類    型
一種函數
作    用
分配內存
最大開闢
128k-16

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最大隻能開闢128k-16,16個字節是被頁描述符結構佔用了。內存映射的I/O口,寄存器或者是硬件設備的RAM(如顯存)一般佔用F0000000以上的地址空間。在驅動程序中不能直接訪問,要通過kernel函數vremap獲得重新映射以後的地址。另外,很多硬件需要一塊比較大的連續內存用作DMA傳送。這塊內存需要一直駐留在內存,不能被交換到文件中去。但是kmalloc最多隻能開闢大小為32XPAGE_SIZE的內存,一般的PAGE_SIZE=4kB,也就是128kB的大小的內存。

kmalloc內存管理

對於提供了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函數分析

kmalloc功能

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() 分配的是物理地址。

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結構

kmalloc和get_free_page申請的內存位於物理內存映射區域,而且在物理上也是連續的,它們與真實的物理地址只有一個固定的偏移,因此存在較簡單的轉換關係,virt_to_phys()可以實現內核虛擬地址轉化為物理地址:
#define __pa(x) ((unsigned long)(x)-PAGE_OFFSET)
extern inline unsigned long virt_to_phys(volatile void * address)
{
return __pa(address);
}
上面轉換過程是將虛擬地址減去3G(PAGE_OFFSET=0XC000000)。與之對應的函數為phys_to_virt(),將內核物理地址轉化為虛擬地址:
#define __va(x) ((void *)((unsigned long)(x)+PAGE_OFFSET))
extern inline void * phys_to_virt(unsigned long address)
{
    return __va(address);
}
virt_to_phys()和phys_to_virt()都定義在include\asm-i386\io.h中。
運行一次上述程序,發現pagemem的地址在0xc7997000(約3G+121M)、kmallocmem 地址在0xc9bc1380(約3G+155M)、vmallocmem的地址在0xcabeb000(約3G+171M)處,符合前文所述的內存佈局。