日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux内存管理和分析vmalloc使用的地址范围

發布時間:2023/12/9 linux 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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為內核規定的一個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大小的空閑區間隔,以防止越界——見下圖)。與進程虛擬地址的特性一樣,這些虛擬地址與物理內存沒有簡單的位移關系,必須通過內核頁表才可轉換為物理地址或物理頁。它們有可能尚未被映射,在發生缺頁時才真正分配物理頁面。

?

Source

?

一、內存管理單元MMU MMU輔助操作系統進行內存管理、提供虛擬地址和物理地址的映射、內存訪問權限保護和Cache緩存控制等硬件支持,可見,這將使得Linux操作系統能單獨為系統的每個用戶分配獨立的內存空間并保證用戶空間不能訪問內核空間的地址,為操作系統的虛擬內存管理模塊提供了硬件基礎。 在s3c2410的vivi這個bootloader中,建立了一個4GB物理地址與虛擬地址一一映射的一級頁表,我們可以通過函數mem_mapping_linear()來探尋一下其創建過程

static?inline?void?mem_mapping_linear(void)
{
??unsigned?long?pageoffset,?sectionNumber;
??/*4GB虛擬地址映射到相應的物理地址上,均不能緩存*/
??for?(sectionNumber?=?0;?sectionNumber?<?4096;?sectionNumber++)
??{
????pageoffset?=?(sectionNumber?<<?20);
????*(mmu_tlb_base?+?(pageoffset?>>?20))?=?pageoffset?|?MMU_SECDESC;
????//mmu_tlb_base為存放頁表的首地址,tlb是轉換旁路緩存,是轉換表的Cache
??}
??/*使能DRAM的區域可緩存*/
??/*SDRAM物理地址0x30000000-0x33ffffff,DRAM_BASE=0x30000000,DRAM_SIZE=64M*/
??for?(pageoffset?=?DRAM_BASE;?pageoffset?<?(DRAM_BASE?+?DRAM_SIZE);?pageoffset?+=?SZ_1M)
??{
????*(mmu_tlb_base?+?(pageoffset?>>?20))?=?pageoffset?|?MMU_SECDESC?|?MMU_CACHEEABLE;
??}
}

??? 這里使用了ARM920T內存映射的Section模式(實際等同于頁大小為1MB的情況),將4GB的虛擬內存空間分為4096個段,因此我們用4096個描述符來對這組段進行描述。這4096個描述符構成的表格就是轉換表,保存在MMU的TLB中。

二、內核空間內存動態申請 在Linux內核空間申請內存涉及的函數主要包括kmalloc()、__get_free_pages()和vmalloc()。kmalloc()、__get_free_pages()申請的內存位于物理內存映射區域,而且物理上也是連續的,它們與真實的物理地址只有一個固定的偏移,而vmalloc()在虛擬內存空間給出一塊連續的內存區,實際上,這片連續的虛擬內存在物理內存中并不一定連續。vmalloc()一般用在為較大的順序緩沖區分配內存,vmalloc()的開銷遠大于__get_free_pages(),為了完成vmalloc(),需要建立新的頁表。 另外還有slab和內存池,這里不進行詳述,可參考相關資料。 對于內核內存空間映射區的虛擬內存(如kmalloc分配的內存),使用virt_to_phys()可以實現內核虛擬地址轉化為物理地址,與之對應的函數為phys_to_virt(),它將物理地址轉化為內核虛擬地址。 三、將設備地址映射到用戶空間 一般情況下,用戶空間不會也不應該直接訪問設備的,但是,設備驅動程序中可實現mmap()函數,這個函數可使得用戶空間能直接訪問設備的物理地址。實際上,mmap實現了一個映射過程:將用戶空間的一段內存與設備內存空間相關聯,當用戶訪問用戶空間的這段地址范圍時,事實上轉化成對設備的訪問。 這個特性對顯示設備非常有意義,如果用戶空間可直接用過內存映射訪問顯存的話,屏幕幀的各點像素將不再需要從用戶空間復制到內核空間。 我們看看mmap的系統調用原型:

caddr_t mmap(caddr_t addr,?size_t?len,?int?prot,?int?flags,?int?fd,?off_t offset);

/*

**參數fd為文件描述符,

**len是映射到用戶空間的字節數,它從被映射文件開頭offset開始算起

**prot指定訪問權限,PROT_READ(可讀)、PROT_WRITE(可寫)、PROT_EXEC(可執行)、PROT_NONE(不可訪問)

**參數addr指定文件應被映射到用戶空間的起始地址,一般為NULL,這樣起始地址的任務將由內核完成,而函數返回值就是映射到用戶空間的地址

*/

??? 當用戶調用mmap()時,內核會進行如下處理:

1、在進程的虛擬空間查找一塊VMA

2、將這塊VMA進行映射到設備地址空間,如果file_operations定義了mmap()操作,則調用它

3、將這個VMA插入到進程的VMA鏈表中

??? vm_operations_struct操作范例,取自fbmem.c

?

static?int
fb_mmap(struct?file?*file,?struct?vm_area_struct?*?vma)
{
????int?fbidx?=?iminor(file->f_path.dentry->d_inode);
????struct?fb_info?*info?=?registered_fb[fbidx];
????struct?fb_ops?*fb?=?info->fbops;
????unsigned?long?off;
????unsigned?long?start;
????u32 len;

????if?(vma->vm_pgoff?>?(~0UL?>>?PAGE_SHIFT))
????????return?-EINVAL;
????off?=?vma->vm_pgoff?<<?PAGE_SHIFT;
????if?(!fb)
????????return?-ENODEV;
????if?(fb->fb_mmap)?{
????????int?res;
????????lock_kernel();
????????res?=?fb->fb_mmap(info,?vma);
????????unlock_kernel();
????????return?res;
????}

????lock_kernel();

????/* frame buffer memory */
????start?=?info->fix.smem_start;
????len?=?PAGE_ALIGN((start?&?~PAGE_MASK)?+?info->fix.smem_len);
????if?(off?>=?len)?{
????????/* memory mapped io */
????????off?-=?len;
????????if?(info->var.accel_flags)?{
????????????unlock_kernel();
????????????return?-EINVAL;
????????}
????????start?=?info->fix.mmio_start;
????????len?=?PAGE_ALIGN((start?&?~PAGE_MASK)?+?info->fix.mmio_len);
????}
????unlock_kernel();
????start?&=?PAGE_MASK;
????if?((vma->vm_end?-?vma->vm_start?+?off)?>?len)
????????return?-EINVAL;
????off?+=?start;
????vma->vm_pgoff?=?off?>>?PAGE_SHIFT;
????/* This is an IO map - tell maydump to skip this VMA */
????vma->vm_flags?|=?VM_IO?|?VM_RESERVED;
????fb_pgprotect(file,?vma,?off);
????if?(io_remap_pfn_range(vma,?vma->vm_start,?off?>>?PAGE_SHIFT,
???????????? vma->vm_end?-?vma->vm_start,?vma->vm_page_prot))
????????return?-EAGAIN;
????return?0;
}

/*

**這段代碼的核心是io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, vma->vm_end - vma->vm_start, vma->vm_page_prot))
**
其中vma->vm_start就是用戶內存映射開始處的虛擬地址
**
vma->vm_end - vma->vm_start是映射的虛擬地址范圍
**
而off >> PAGE_SHIFT是虛擬地址應該映射到的物理地址off的頁幀號,實際上就是物理地址off右移了PAGE_SHIFT位:
off = vma->vm_pgoff << PAGE_SHIFT;
start = info->fix.smem_start;//smem_start是顯存的起始物理地址
start &= PAGE_MASK;
off += start;
**從上述過程可以看出,將顯存的物理地址的頁幀號映射到用戶空間的虛擬地址上

**mmap必須以PAGE_SIZE為單位進行映射,實際上內存只能以頁為單位進行映射,如果非PAGE_SIZE整數倍的地址范圍,要先進行頁對齊,強行以PAGE_SIZE的倍數大小進行映射

*/

在驅動程序中,我們能使用remap_pfn_range()映射內存中的保留頁和設備IO內存,另外kmalloc申請的內存若要被映射到用戶空間可以通過mem_map_reserve()設置為保留后進行。這個特性可用于用戶程序要頻繁將數據寫到設備中的buffer時,這樣可以減少系統read、write等調用開銷。 【Note】:mem_map_reserve是2.4版本內核的函數,2.6內核用SetPageReserved取代之 映射kmalloc申請的內存到用戶空間范例

/*內核模塊加載函數*/
int?
__init kmalloc_map_init(void)
{
????//申請設備號,添加cdev結構體
????buffer?=?kmalloc(BUFFER_SIZE,?GFP_KERNEL);
????for?(page?=?virt_to_page(buffer);?page?<?virt_to_page(buffer?+?BUFFER_SIZE);?page++)
????{
????????mem_map_reserve(page);//置頁為保留,virt_to_page()將內核虛擬地址轉化為頁
????}
}
/*mmap()函數*/
static?int?kmalloc_map_mmap(struct?file?*filp,?struct?vm_area_struct?*vma)
{
????unsigned?long?page,?pos;
????unsigned?long?start?=?(unsigned?long)vma->vm_start;
????unsigned?long?size?=?(unsigned?long)(vma->vm_end?-?vma->vm_start);
????if?(size?>?BUFFER_SIZE)
????{
????????return?-?EINVAL;
????}
????pos?=?(unsigned?long)buffer;
????/*映射buffer中的所有頁*/
????while?(size?>?0)
????{
????????page?=?virt_to_phys((void?*)pos);
????????if?(remap_page_range(start,?page,?PAGE_SIZE,?PAGE_SHARED))
????????????return?-?EAGAIN;
????????start?+=?PAGE_SIZE;
????????pos?+=?PAGE_SIZE;
????????size?-=?PAGE_SIZE;
????}

/*

**可否用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的實現,見Linux kernel change log:?Changes remap_page_range to remap_pfn_range for 2.6.10 and above kernels

*/
????return?0;
}

以下有關內存管理的內容轉自frank_seng大俠的帖子:http://linux.chinaunix.net/bbs/viewthread.php?tid=1003872&extra=page%3D2%26amp%3Bfilter%3Ddigest 說得極其簡煉易懂,我實在非常佩服。 1. 內核初始化:

? ? * 內核建立好內核頁目錄頁表數據庫,假設物理內存大小為len,則建立了[3G--3G+len]::[0--len]這樣的虛地址vaddr和物理地址paddr的線性對應關系;
? ? * 內核建立一個page數組,page數組和物理頁面系列完全是線性對應,page用來管理該物理頁面狀態,每個物理頁面的虛地址保存在page->virtual中;
? ? * 內核建立好一個free_list,將沒有使用的物理頁面對應的page放入其中,已經使用的就不用放入了;

2. 內核模塊申請內存vaddr = get_free_pages(mask,order):

? ? * 內存管理模塊從free_list找到一個page,將page->virtual作為返回值,該返回值就是對應物理頁面的虛地址;
? ? * 將page從free_list中脫離;
? ? * 模塊使用該虛擬地址操作對應的物理內存;

3. 內核模塊使用vaddr,例如執行指令mov(eax, vaddr):

? ? * CPU獲得vaddr這個虛地址,利用建立好的頁目錄頁表數據庫,找到其對應的物理內存地址;
? ? * 將eax的內容寫入vaddr對應的物理內存地址內;

4. 內核模塊釋放內存free_pages(vaddr,order):

? ? * 依據vaddr找到對應的page;
? ? * 將該page加入到free_list中;

5. 用戶進程申請內存vaddr = malloc(size):

? ? * 內存管理模塊從用戶進程內存空間(0--3G)中找到一塊還沒使用的空間vm_area_struct(start--end);
? ? * 隨后將其插入到task->mm->mmap鏈表中;

6. 用戶進程寫入vaddr(0-3G),例如執行指令mov(eax, vaddr):

? ? * CPU獲得vaddr這個虛地址,該虛地址應該已經由glibc庫設置好了,一定在3G一下的某個區域,根據CR3寄存器指向的current->pgd查當前進程的頁目錄頁表數據庫,發現該vaddr對應的頁目錄表項為0,故產生異常;
? ? * 在異常處理中,發現該vaddr對應的vm_area_struct已經存在,為vaddr對應的頁目錄表項分配一個頁表;
? ? * 隨后從free_list找到一個page,將該page對應的物理頁面物理首地址賦給vaddr對應的頁表表項,很明顯,此時的vaddr和paddr不是線性對應關系了;
? ? * 將page從free_list中脫離;
? ? * 異常處理返回;
? ? * CPU重新執行剛剛發生異常的指令mov(eax, vaddr);
? ? * CPU獲得vaddr這個虛地址,根據CR3寄存器指向的current->pgd,利用建立好的頁目錄頁表數據庫,找到其對應的物理內存地址;
? ? * 將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對應的物理內存;
? ? * 頁目錄頁表數據庫的主要目的是為CPU訪問物理內存時轉換vaddr-->paddr使用,分配以及釋放內存時不會用到,但是需要內核內存管理系統在合適時機為CPU建立好該庫;
? ? * 對于用戶進程在6中獲得的物理頁面,有兩個頁表項對應,一個就是內核頁目錄頁表數據庫的某個pte[i ],一個就是當前進程內核頁目錄頁表數據庫的某個 pte[j],但是只有一個page和其對應。如果此時調度到其他進程,其他進程申請并訪問某個內存,則不會涉及到該物理頁面,因為其分配時首先要從 free_list中找一個page,而該物理頁面對應的page已經從free_list中脫離出來了,因此不存在該物理頁面被其他進程改寫操作的情況。內核中通過get_free_pages等方式獲取內存時,也不會涉及到該物理頁面,原理同前所述。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎

總結

以上是生活随笔為你收集整理的Linux内存管理和分析vmalloc使用的地址范围的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。