Vmalloc可以獲得的地址在VMALLOC_START到VMALLOC_END的范圍中。這兩個符號在<asm/pgtable.h>中定義:
/* include/asm/pgtable.h */
#define VMALLOC_OFFSET (8*1024*1024)
#define VMALLOC_START (((unsigned long)high_memory + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
…………
high_memory值在這裡定義:
/* arch/arm/mm/init.c */
void __init bootmem_init(struct meminfo *mi)
{
……
high_memory = __va(memend_pfn << PAGE_SHIFT);
}
在我們的板子上,這些值為:
high_mem = 0xc4000000 <--------- 3G+64M , high_memory既實際內存最大物理地址對應的的內核邏輯地址
VMALLOC_START = 0xc4800000 <--------- 3G+64M+8M (8M為內核規定的一個gap) ,vmalloc分配的起始地址(內核空間)
我在kernel裡加了一些打印信息,打印出的結果如下:
Starting kernel ...
Linux version 2.6.18_pro500-omap5912_osk (root@Ubuntu) (gcc version 4.2.0 20070319 (prerelease) (MontaVista 4.2.0-4.0.0.0702865 2007-03-26)) #36 Mon Jun 16 16:29:30 CST 2008
CPU: ARM926EJ-S [41069265] revision 5 (ARMv5TEJ), cr=00053177
Machine:
Memory policy: ECC disabled, Data cache writeback
high_mem = 0xc4000000 --------------------
vmalloc_start = 0xc4800000 ----------------------
…………
The following is the vmalloc test processing:
/* Vmalloc Test Module */
……
static int __init tcm_init(void)
{
struct resource * ret;
unsigned long * vaddr1 = NULL;
unsigned long * vaddr2 = NULL;
……
vaddr1 = vmalloc ( PAGE_SIZE );
printk("vaddr1 = 0x%p \n", vaddr1);
vaddr2 = vmalloc ( PAGE_SIZE );
printk("vaddr2 = 0x%p \n", vaddr2);
vfree(vaddr1);
vfree(vaddr2);
……
}
……
module_init(tcm_init);
module_exit(tcm_exit);
The running result:
# insmod tcm1.ko
vaddr1 = 0xc487a000 ? vmalloc分配的地址,大於0xc3ffffff (3G+64M)
vaddr2 = 0xc487c000
參考資料: (摘自《Linux 內存管理》)
……
vmalloc分配的內核虛擬內存與kmalloc/get_free_page分配的內核虛擬內存位於不同的區間,不會重疊。因為內核虛擬空間被分區管理,各司其職。進程空間地址分布從0到3G(其實是到PAGE_OFFSET, 在0x86中它等於0xC0000000),從3G到vmalloc_start這段地址是物理內存映射區域(該區域中包含了內核鏡像、物理頁面表mem_map等等)比如我使用的系統內存是64M(可以用free看到),那麼(3G——3G+64M)這片內存就應該映射到物理內存,而vmalloc_start位置應在3G+64M附近(說"附近"因為是在物理內存映射區與vmalloc_start期間還會存在一個8M大小的gap來防止躍界),vmalloc_end的位置接近4G(說"接近"是因為最
後位置系統會保留一片128k大小的區域用於專用頁面映射,還有可能會有高端內存映射區,這些都是細節,這裡我們不做糾纏)。
由get_free_page或Kmalloc函數所分配的連續內存都陷於物理映射區域,所以它們返回的內核虛擬地址和實際物理地址僅僅是相差一個偏移量(PAGE_OFFSET),你可以很方便的將其轉化為物理內存地址,同時內核也提供了virt_to_phys()函數將內核虛擬空間中的物理映射區地址轉化為物理地址。要知道,物理內存映射區中的地址與內核頁表是有序對應的,系統中的每個物理頁面都可以找到它對應的內核虛擬地址(在物理內存映射區中的)。
而vmalloc分配的地址則限於vmalloc_start與vmalloc_end之間。每一塊vmalloc分配的內核虛擬內存都對應一個vm_struct結構體(可別和vm_area_struct搞混,那可是進程虛擬內存區域的結構),不同的內核虛擬地址被4k大小的空閒區間隔,以防止越界——見下圖)。與進程虛擬地址的特性一樣,這些虛擬地址與物理內存沒有簡單的位移關系,必須通過內核頁表才可轉換為物理地址或物理頁。它們有可能尚未被映射,在發生缺頁時才真正分配物理頁面。