1. 程式人生 > >kmalloc、vmalloc、malloc的區別

kmalloc、vmalloc、malloc的區別

簡單的說:

  1. kmalloc和vmalloc是分配的是核心的記憶體,malloc分配的是使用者的記憶體
  2. kmalloc保證分配的記憶體在物理上是連續的,vmalloc保證的是在虛擬地址空間上的連續,malloc不保證任何東西(這點是自己猜測的,不一定正確)
  3. kmalloc能分配的大小有限,vmalloc和malloc能分配的大小相對較大
  4. 記憶體只有在要被DMA訪問的時候才需要物理上連續
  5. vmalloc比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和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中。

而vmalloc申請的記憶體則位於vmalloc_start~vmalloc_end之間,與實體地址沒有簡單的轉換關係,雖然在邏輯上它們也是連續的,但是在物理上它們不要求連續。

我們用下面的程式來演示kmalloc、get_free_page和vmalloc的區別:
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
MODULE_LICENSE("GPL");
unsigned char *pagemem;
unsigned char *kmallocmem;
unsigned char *vmallocmem;

int __init mem_module_init(void)
{
//最好每次記憶體申請都檢查申請是否成功
//下面這段僅僅作為演示的程式碼沒有檢查
pagemem = (unsigned char*)get_free_page(0);
printk("<1>pagemem addr=%x", pagemem);

kmallocmem = (unsigned char*)kmalloc(100, 0);
printk("<1>kmallocmem addr=%x", kmallocmem);

vmallocmem = (unsigned char*)vmalloc(1000000);
printk("<1>vmallocmem addr=%x", vmallocmem);

return 0;
}

void __exit mem_module_exit(void)
{
free_page(pagemem);
kfree(kmallocmem);
vfree(vmallocmem);
}

module_init(mem_module_init);
module_exit(mem_module_exit);

我們的系統上有160MB的記憶體空間,執行一次上述程式,發現pagemem的地址在0xc7997000(約3G+121M)、kmallocmem 地址在0xc9bc1380(約3G+155M)、vmallocmem的地址在0xcabeb000(約3G+171M)處,符合前文所述的記憶體佈局。