Linux内存管理和分析vmalloc使用的地址范围
From: http://www.cnblogs.com/dubingsky/archive/2010/04/20/1716158.html
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為內核規(guī)定的一個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分配的內核虛擬內存位于不同的區(qū)間,不會重疊。因為內核虛擬空間被分區(qū)管理,各司其職。進程空間地址分布從0到3G(其實是到PAGE_OFFSET, 在0x86中它等于0xC0000000),從3G到vmalloc_start這段地址是物理內存映射區(qū)域(該區(qū)域中包含了內核鏡像、物理頁面表mem_map等等)比如我使用的系統(tǒng)內存是64M(可以用free看到),那么(3G——3G+64M)這片內存就應該映射到物理內存,而vmalloc_start位置應在3G+64M附近(說"附近"因為是在物理內存映射區(qū)與vmalloc_start期間還會存在一個8M大小的gap來防止躍界),vmalloc_end的位置接近4G(說"接近"是因為最
后位置系統(tǒng)會保留一片128k大小的區(qū)域用于專用頁面映射,還有可能會有高端內存映射區(qū),這些都是細節(jié),這里我們不做糾纏)。
?
?
?
由get_free_page或Kmalloc函數(shù)所分配的連續(xù)內存都陷于物理映射區(qū)域,所以它們返回的內核虛擬地址和實際物理地址僅僅是相差一個偏移量(PAGE_OFFSET),你可以很方便的將其轉化為物理內存地址,同時內核也提供了virt_to_phys()函數(shù)將內核虛擬空間中的物理映射區(qū)地址轉化為物理地址。要知道,物理內存映射區(qū)中的地址與內核頁表是有序對應的,系統(tǒng)中的每個物理頁面都可以找到它對應的內核虛擬地址(在物理內存映射區(qū)中的)。
而vmalloc分配的地址則限于vmalloc_start與vmalloc_end之間。每一塊vmalloc分配的內核虛擬內存都對應一個vm_struct結構體(可別和vm_area_struct搞混,那可是進程虛擬內存區(qū)域的結構),不同的內核虛擬地址被4k大小的空閑區(qū)間隔,以防止越界——見下圖)。與進程虛擬地址的特性一樣,這些虛擬地址與物理內存沒有簡單的位移關系,必須通過內核頁表才可轉換為物理地址或物理頁。它們有可能尚未被映射,在發(fā)生缺頁時才真正分配物理頁面。
?
Source?
一、內存管理單元MMU MMU輔助操作系統(tǒng)進行內存管理、提供虛擬地址和物理地址的映射、內存訪問權限保護和Cache緩存控制等硬件支持,可見,這將使得Linux操作系統(tǒng)能單獨為系統(tǒng)的每個用戶分配獨立的內存空間并保證用戶空間不能訪問內核空間的地址,為操作系統(tǒng)的虛擬內存管理模塊提供了硬件基礎。 在s3c2410的vivi這個bootloader中,建立了一個4GB物理地址與虛擬地址一一映射的一級頁表,我們可以通過函數(shù)mem_mapping_linear()來探尋一下其創(chuàng)建過程| static?inline?void?mem_mapping_linear(void) |
??? 這里使用了ARM920T內存映射的Section模式(實際等同于頁大小為1MB的情況),將4GB的虛擬內存空間分為4096個段,因此我們用4096個描述符來對這組段進行描述。這4096個描述符構成的表格就是轉換表,保存在MMU的TLB中。
二、內核空間內存動態(tài)申請 在Linux內核空間申請內存涉及的函數(shù)主要包括kmalloc()、__get_free_pages()和vmalloc()。kmalloc()、__get_free_pages()申請的內存位于物理內存映射區(qū)域,而且物理上也是連續(xù)的,它們與真實的物理地址只有一個固定的偏移,而vmalloc()在虛擬內存空間給出一塊連續(xù)的內存區(qū),實際上,這片連續(xù)的虛擬內存在物理內存中并不一定連續(xù)。vmalloc()一般用在為較大的順序緩沖區(qū)分配內存,vmalloc()的開銷遠大于__get_free_pages(),為了完成vmalloc(),需要建立新的頁表。 另外還有slab和內存池,這里不進行詳述,可參考相關資料。 對于內核內存空間映射區(qū)的虛擬內存(如kmalloc分配的內存),使用virt_to_phys()可以實現(xiàn)內核虛擬地址轉化為物理地址,與之對應的函數(shù)為phys_to_virt(),它將物理地址轉化為內核虛擬地址。 三、將設備地址映射到用戶空間 一般情況下,用戶空間不會也不應該直接訪問設備的,但是,設備驅動程序中可實現(xiàn)mmap()函數(shù),這個函數(shù)可使得用戶空間能直接訪問設備的物理地址。實際上,mmap實現(xiàn)了一個映射過程:將用戶空間的一段內存與設備內存空間相關聯(lián),當用戶訪問用戶空間的這段地址范圍時,事實上轉化成對設備的訪問。 這個特性對顯示設備非常有意義,如果用戶空間可直接用過內存映射訪問顯存的話,屏幕幀的各點像素將不再需要從用戶空間復制到內核空間。 我們看看mmap的系統(tǒng)調用原型:| caddr_t mmap(caddr_t addr,?size_t?len,?int?prot,?int?flags,?int?fd,?off_t offset); /* **參數(shù)fd為文件描述符, **len是映射到用戶空間的字節(jié)數(shù),它從被映射文件開頭offset開始算起 **prot指定訪問權限,PROT_READ(可讀)、PROT_WRITE(可寫)、PROT_EXEC(可執(zhí)行)、PROT_NONE(不可訪問) **參數(shù)addr指定文件應被映射到用戶空間的起始地址,一般為NULL,這樣起始地址的任務將由內核完成,而函數(shù)返回值就是映射到用戶空間的地址 */ |
??? 當用戶調用mmap()時,內核會進行如下處理:
| 1、在進程的虛擬空間查找一塊VMA 2、將這塊VMA進行映射到設備地址空間,如果file_operations定義了mmap()操作,則調用它 3、將這個VMA插入到進程的VMA鏈表中 |
??? vm_operations_struct操作范例,取自fbmem.c
?
| static?int /* **這段代碼的核心是io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot)) **mmap必須以PAGE_SIZE為單位進行映射,實際上內存只能以頁為單位進行映射,如果非PAGE_SIZE整數(shù)倍的地址范圍,要先進行頁對齊,強行以PAGE_SIZE的倍數(shù)大小進行映射 */ |
| /*內核模塊加載函數(shù)*/ /* **可否用io_remap_pfn_range(vma, vma->vm_start, virt_to_phys((void?*)buffer)?>> PAGE_SHIFT, vma->vm_end - vma->vm_start, PAGE_SHARED)來替代remap_page_range? **在Linux kernel 2.6.27中,已經找不到remap_page_range的實現(xiàn),見Linux kernel change log:?Changes remap_page_range to remap_pfn_range for 2.6.10 and above kernels */ |
? ? * 內核建立好內核頁目錄頁表數(shù)據(jù)庫,假設物理內存大小為len,則建立了[3G--3G+len]::[0--len]這樣的虛地址vaddr和物理地址paddr的線性對應關系;
? ? * 內核建立一個page數(shù)組,page數(shù)組和物理頁面系列完全是線性對應,page用來管理該物理頁面狀態(tài),每個物理頁面的虛地址保存在page->virtual中;
? ? * 內核建立好一個free_list,將沒有使用的物理頁面對應的page放入其中,已經使用的就不用放入了;
2. 內核模塊申請內存vaddr = get_free_pages(mask,order):
? ? * 內存管理模塊從free_list找到一個page,將page->virtual作為返回值,該返回值就是對應物理頁面的虛地址;
? ? * 將page從free_list中脫離;
? ? * 模塊使用該虛擬地址操作對應的物理內存;
3. 內核模塊使用vaddr,例如執(zhí)行指令mov(eax, vaddr):
? ? * CPU獲得vaddr這個虛地址,利用建立好的頁目錄頁表數(shù)據(jù)庫,找到其對應的物理內存地址;
? ? * 將eax的內容寫入vaddr對應的物理內存地址內;
4. 內核模塊釋放內存free_pages(vaddr,order):
? ? * 依據(jù)vaddr找到對應的page;
? ? * 將該page加入到free_list中;
5. 用戶進程申請內存vaddr = malloc(size):
? ? * 內存管理模塊從用戶進程內存空間(0--3G)中找到一塊還沒使用的空間vm_area_struct(start--end);
? ? * 隨后將其插入到task->mm->mmap鏈表中;
6. 用戶進程寫入vaddr(0-3G),例如執(zhí)行指令mov(eax, vaddr):
? ? * CPU獲得vaddr這個虛地址,該虛地址應該已經由glibc庫設置好了,一定在3G一下的某個區(qū)域,根據(jù)CR3寄存器指向的current->pgd查當前進程的頁目錄頁表數(shù)據(jù)庫,發(fā)現(xiàn)該vaddr對應的頁目錄表項為0,故產生異常;
? ? * 在異常處理中,發(fā)現(xiàn)該vaddr對應的vm_area_struct已經存在,為vaddr對應的頁目錄表項分配一個頁表;
? ? * 隨后從free_list找到一個page,將該page對應的物理頁面物理首地址賦給vaddr對應的頁表表項,很明顯,此時的vaddr和paddr不是線性對應關系了;
? ? * 將page從free_list中脫離;
? ? * 異常處理返回;
? ? * CPU重新執(zhí)行剛剛發(fā)生異常的指令mov(eax, vaddr);
? ? * CPU獲得vaddr這個虛地址,根據(jù)CR3寄存器指向的current->pgd,利用建立好的頁目錄頁表數(shù)據(jù)庫,找到其對應的物理內存地址;
? ? * 將eax的內容寫入vaddr對應的物理內存地址內;??
7. 用戶進程釋放內存vaddr,free(vaddr):
? ? * 找到該vaddr所在的vm_area_struct;
? ? * 找到vm_area_struct:start--end對應的所有頁目錄頁表項,清空對應的所有頁表項;
? ? * 釋放這些頁表項指向物理頁面所對應的page,并將這些page加入到free_list隊列中;
? ? * 有必要還會清空一些頁目錄表項,并釋放這些頁目錄表項指向的頁表;
? ? * 從task->mm->mmap鏈中刪除該vm_area_struct并釋放掉;
綜合說明:
? ? * 可用物理內存就是free_list中各page對應的物理內存;
? ? * 頁目錄頁表數(shù)據(jù)庫的主要目的是為CPU訪問物理內存時轉換vaddr-->paddr使用,分配以及釋放內存時不會用到,但是需要內核內存管理系統(tǒng)在合適時機為CPU建立好該庫;
? ? * 對于用戶進程在6中獲得的物理頁面,有兩個頁表項對應,一個就是內核頁目錄頁表數(shù)據(jù)庫的某個pte[i ],一個就是當前進程內核頁目錄頁表數(shù)據(jù)庫的某個 pte[j],但是只有一個page和其對應。如果此時調度到其他進程,其他進程申請并訪問某個內存,則不會涉及到該物理頁面,因為其分配時首先要從 free_list中找一個page,而該物理頁面對應的page已經從free_list中脫離出來了,因此不存在該物理頁面被其他進程改寫操作的情況。內核中通過get_free_pages等方式獲取內存時,也不會涉及到該物理頁面,原理同前所述。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎
總結
以上是生活随笔為你收集整理的Linux内存管理和分析vmalloc使用的地址范围的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux高级文本处理之sed(三)
- 下一篇: 项目经理和产品经理的区别