3、非連續(xù)內存區(qū)管理
按需網站策劃可以根據自己的需求進行定制,成都網站制作、網站設計構思過程中功能建設理應排到主要部位公司成都網站制作、網站設計的運用實際效果公司網站制作網站建立與制做的實際意義如果對內存區(qū)的請求不是很頻繁,那么,通過連續(xù)的線性地址來訪問非連續(xù)的頁框這樣一種分配模式就很有意義。這種模式的主要優(yōu)點是避免了外碎片,而缺點是必須打亂內核頁表。非連續(xù)內存區(qū)的大小必須是4096的倍數。linux在幾個方面使用非連續(xù)內存區(qū),例如,為活動的交換區(qū)分配數據結構,為模塊分配空間,或給某些IO驅動程序分配緩沖區(qū)。此外,非連續(xù)內存區(qū)還提供了另一種使用高端內存頁框的方法。
3.1 分連續(xù)內存區(qū)的線性地址
要查找一個線性地址的空閑區(qū),從PAGE_OFFSET開始查找,即第4個GB的其實地址
1、內存區(qū)的開始部分包含的是是對前896MB RAM進行映射的線性地址;直接映射的物理內存末尾多對應的線性地址保存在high_memory變量中。
2、內存區(qū)的結尾部分包含的是固定映射線性地址。
3、從PKMAP_BASE開始,我們查找用于高端內存頁框的永久內核映射的線性地址
4、其余的線性地址可以用于非連續(xù)內存區(qū)。在物理內存映射的末尾與第一個內存區(qū)之間插入一個大小為8MB的安全區(qū),目的是為了捕獲對內存的越界訪問。處于同樣的理由,插入其他4KB大小的安全區(qū)來隔離非連續(xù)內存區(qū)。為非連續(xù)內存區(qū)保留的線性地址空間的起始地址由VMALLOC_START宏定義,而末尾由VMALLOC_END宏定義
3.2 非連續(xù)內存區(qū)描述符
struct vm_struct{
void * addr;//內存區(qū)第一個內存單元的地址
unsigned long size; //內存區(qū)的大小加上4k隔離區(qū)的值
unsigned long flags; //非連續(xù)內存區(qū)映射的內存的類型
struct page ** pages; //非連續(xù)內存區(qū)包含的頁描述符數組
unsigned int nr_pages;//頁數量
unsigned long phys_addr;//該字段設為0,除非內存已被創(chuàng)建來映射一個硬件設備的IO共享內存
struct vm_struct * next;//指向下一個vm_struct結構的指針
}
flags字段標識了非連續(xù)區(qū)映射內存的類型:VM_ALLOC表示使用vmalloc()得到的頁,VM_MAP表示使用vmap()映射已經分配的頁,VM_IOREMAP表示ioremap()映射的硬件設備的板上內存。
get_vm_area()函數在線性地址VMALLOC_START和VMALLOC_END之間查找一個空閑區(qū)域。函數使用size和flag兩個參數,步驟如下:
1、調用kmalloc()為vm_struct類型的新描述符獲得一個內存區(qū)
2、得到vmlist_lock鎖,并掃描類型為vm_struct的描述符鏈表來查找線性地址的一個空閑區(qū)域,至少覆蓋size+4096個地址。
3、如果存在這樣一個區(qū)間,函數就初始化描述符字段,釋放vmlist_lock鎖,返回非連續(xù)內存區(qū)描述符的起始地址。
4、否則釋放描述符,釋放鎖,返回NULL
3.3 分配非連續(xù)內存區(qū)
vmalloc()函數給內核分配一個非連續(xù)內存區(qū),參數size表示所請求的內存區(qū)大小
void *vmalloc(unsigned long size){
struct vm_struct *area;
struct page **pages;
unsigned int array_size,i;
size = (size +PAGE_SIZE -1)&PAGE_MASK;
area = get_vm_area(size,VM_ALLOC);
if(!area)
return NULL;
area->nr_pages =size>>PAGE_SHIFT;
array_size = (area->nr_pages *sizeof(struct page *));
area->pages = pages = kmalloc(array_size,GFP_KERNEL);
if(!area->pages){
remove_vm_area(area->addr);
kfree(area);
return NULL;
}
memset(area->pages,0,array_size);
for(i=0;i
area->pages[i] = alloc_page(GFP_KERNEL|__GFP_HIGHMEM);
if(!area->pages[i]){
area->nr_pages = i;
fail:vfree(area->addr);
return NULL:
}
}
if(map_vm_area(area,__pgprot(0x63),&pages));
goto fail;
return area->addr;
}
map_vm_area(struct vm_struct * area,prot,struct page ** pages){
unsigned long address,end;
address = area->addr;
end = address+(area->size-PAGE_SIZE);
pgd = pgd_offset_k(address);//主內核頁全局目錄中的目錄項,該項對應于內存區(qū)其實線性地址
spin_lock(&init_mm.page_table_lock);//獲得內核頁表自旋鎖
int ret = 0;
for(i=pgd_index(address);i pud_t *pud = pud_alloc(&init_mm,pgd,address);//為新的內存區(qū)創(chuàng)建一個頁上級目錄,并把它的物理地址寫入內核全局目錄的合適表項。然后調用alloc_area_pud()為新的頁上級目錄分配所有相關的頁表。 ret = -ENOMEM; if(!pud) break; next = (address + PGDIR_SIZE)&PGDIR_MASK; if(next
next = end;
if(map_area_pud(pud,address,next,prot,pages))
break;
address = next;
pgd++;
ret = 0;
}
map_area_pud():
do{
pmd_t *pmd = pmd_alloc(&init_mm,pud,address);
if(!pmd)
return -ENOMEM;
if(map_area_pmd(pmd,address,end-address,prot,pages)){
return -ENOMEM;
address = (address + PUD_SIZE) & PUD_MASK;
pud ++;
}while(address map_area_pmd(): do{ pte_t *pte = pte_alloc_kernel(&init_mm,pmd,address); if(!pte) return -ENOMEM; if(map_area_pte(pte,address,end-address,prot,pages)) return -ENOMEM; address = (address + PMD_SIZE)&PMD_MASK; pmd ++; }while(address map_area_pte(): do{ struct page *page = **pages; set_pte(pte,mk_pte(page,prot); address += PAGE_SIZE; pte++; (*pages)++; }while(address 3.4 釋放非連續(xù)內存區(qū) vfree-----vmalloc()、vmalloc32(); vunmap----vmap(); vfree和vunmap都依賴于__vunmap()函數。 __vunmap()接收兩個參數,將要釋放內存區(qū)的起始地址addr,以及標致deallocate_pages,如果被映射到內存區(qū)內的頁框應當被釋放到頁框分配器,這個標志設置;步驟: 1、調用remove_vm_area()函數得到vm_struct描述符的地址area,并清除內存區(qū)中線性地址對應的內核頁表項。 2、如果deallocate_pages被設置,函數掃描指向頁描述符的area->pages指針數組;對于數組的每一個元素,調用__free_page()函數釋放頁框到分區(qū)分配器。此外,執(zhí)行kfree(area->pages)來釋放數組本身。 3、調用kfree(area)來釋放vm_struct描述符 remove_vm_area(): write_lock(&vmlist_lock); for(p=&vmlist;(tmp=*p);p=&tmp->next){ if(tmp->addr == addr){ unmap_vm_area(tmp); *p = tmp->next; break; } } write_unlock(&vmlist_lock); return tmp; unmap_vm_area(): address = area->addr; end = address +area->size; pgd = pgd_offset_k(address); for(i=pgd_index(address);i<=pgd_index(end);i++){ next = (address+PGDIR_SIZE)&PGDIR_MASK; if(next <=address || next >end); next = end; unmap_area_pud(pgd,address,next-address); address = next; pgd++; } unmap_area_pud(): do{ unmap_area_pmd(pud,address,end-address) address = (address +PUD_SIZE)&PUD_MASK; pud++; }while(address&&(address unmap_area_pmd(): do{ unmap_area_pte(pmd,address,end-address); address = (address + PMD_SIZE)&PMD_MASK; pmd++; }while(address unmap_area_pte(): do{ pte_t page = ptep_get_and_clear(pte); address += PAGE_SIZE; pte++; if(!pte_none(page)&&!pte_present(page)) printk("Whee..Swapped out page in kermel page table
"); }while(address
網站標題:八、內存管理(三)-創(chuàng)新互聯
分享路徑:http://weahome.cn/article/djspej.html