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
|
这里使用了ARM920T内存映射的Section模式(实际等同于页大小为1MB的情况),将4GB的虚拟内存空间分为4096个段,因此我们用4096个描述符来对这组段进行描述。这4096个描述符构成的表格就是转换表,保存在MMU的TLB中。
|
当用户调用mmap()时,内核会进行如下处理:
|
vm_operations_struct操作范例,取自fbmem.c
|
|
* 内核建立好内核页目录页表数据库,假设物理内存大小为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使用的地址范围相关推荐
- linux内存管理、分析、泄露定位与工具整理
linux内存管理.分析.泄露定位与工具整理 linux内存管理相关知识 1. 进程的内存申请与分配 2. 当前系统总内存的统计 linux内存分析 linux内存泄漏相关知识 内存泄露的分类 val ...
- linux内存管理---虚拟地址、逻辑地址、线性地址、物理地址的区别(一)
分析linux内存管理机制,离不了上述几个概念,在介绍上述几个概念之前,先从<深入理解linux内核>这本书中摘抄几段关于上述名词的解释: 一.<深入理解linux内核>的解释 ...
- linux内存管理——kmalloc和vmalloc
直接映射区:线性空间中从3G开始最大896M的区间,为直接内存映射区,该区域的线性地址和物理地址存在线性转换关系:线性地址=3G+物理地址. 动态内存映射区:该区域由内核函数vmalloc来分配,特点 ...
- linux内存管理详解,Linux内存管理图文讲解.pdf
Linux内存管理图文讲解 逻辑地址.线性地址.物理地址和虚拟地址 一.概念 物理地址(physical address) 用于内存芯片级的单元寻址,和处理器和 CPU 连接的地址总线相对应. 这个概 ...
- Linux 内存管理 详解(虚拟内存、物理内存,进程地址空间)
Linux -操作系统内存管理 存储系统 存储器的层次结构 Linux的内存管理 物理内存 物理内存管理 虚拟内存 虚拟地址空间 (写时拷贝) 和物理地址映射关系 页表 虚拟内存优缺点 「在 4GB ...
- Linux内核分析(三)----初识linux内存管理子系统
原文:Linux内核分析(三)----初识linux内存管理子系统 Linux内核分析(三) 昨天我们对内核模块进行了简单的分析,今天为了让我们今后的分析没有太多障碍,我们今天先简单的分析一下linu ...
- Linux内存管理 (透彻分析)
摘要: 本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法.力求从外到内.水到渠成地引导网友分析Linux的内存管理与使用. ...
- linux内核分为子系统,Linux内核内存管理子系统分析【转】
还是那张熟悉的老图:Linux内核子系统简介(由七个部分组成) Linux内存管理模型: 1. 内存管子系统职能: 1> 管理虚拟地址与物理地址的映射 2> 管理物理内存的分配 2. ...
- Linux内存管理 brk(),mmap()系统调用源码分析2:brk()的内存释放流程
Linux brk(),mmap()系统调用源码分析 brk()的内存释放流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...
最新文章
- Abp mysql guid_.NET生成多数据库有序Guid
- CUDA、CUDA toolkit、CUDNN、NVCC关系
- python如何下载tushare_安装tushare
- MySQL dump文件导入
- 用于Power BI Desktop中的库存数据分析的烛台图
- [Lintcode]102. Linked List Cycle/[Leetcode]
- 关于标题的多HTML定制
- css中如何设置字体
- 如何在“活动监视器”中检查Mac是否需要更多内存?
- 全局变量的使用和声明
- iOS字体适配方法总结
- 一款简约大气的PHP短网址系统程序源码
- 软件维护集成价格估算
- BP神经网络简单应用实例,bp神经网络应用举例
- 微软Surface笔记本电脑进入bios界面
- 戏剧之家杂志戏剧之家杂志社戏剧之家编辑部2022年第18期目录
- 日常交通工具日语词汇
- Binder Java层实现(一):IBinder/IInterface/Binder/Stub
- WEKA进行Apriori、FP-Tree、K-means算法测试
- java第三方包_java客户端引入第三方包方法
热门文章
- 高级Python:定义类时要应用的9种最佳做法
- leetcode 149. 直线上最多的点数
- 如何使用React,TypeScript和React测试库创建出色的用户体验
- vue项目示例代码git_您应该了解的5个Git命令以及代码示例
- sql注入语句示例大全_SQL Order By语句:示例语法
- fritz 使用手册_Fritz对象检测指南:使用机器学习在Android中构建宠物监控应用
- 如何使用TensorFlow构建简单的图像识别系统(第2部分)
- 【0718作业】收集和整理面向对象的六大设计原则
- 数据源 连接oracle
- 2019春季学期进度报告(十四)