当前位置: 首页 > news >正文

Linux 虚拟地址与物理地址的映射关系分析【转】

 

目录(?)[-]

  1. 虚拟空间
  2. 进程虚拟地址的组织
    1. 1 虚拟空间用户空间
    2. 2 内存区间
  3. 系统物理地址的组织
    1. 1 用户空间页面目录映射关系
    2. 2用户空间的映射
    3. 3内核空间虚拟地址的映射
  4. 相关数据结构关系图
 

Ordeder原创文章,原文链接: http://blog.csdn.NET/ordeder/article/details/41630945

源码版本 2.4.0

1. 虚拟空间

 

0-3G 用户空间  0x00000000  ~ 0xbfffffff

3-4G 内核空间     0xc0000000 ~ 0xffffffff

每个用户进程都有独立的用户空间(虚拟地址0-3),而内核空间是唯一的(相当于共享)

每个进程的用户空间用mm_struct描述,即task_struct.mm。

2.进程虚拟地址的组织

2.1 虚拟空间、用户空间

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. struct mm_struct {  
  2.     struct vm_area_struct * mmap;   /* list of VMAs */  
  3.     ...  
  4.     pgd_t * pgd;                //用于地址映射  
  5.     atomic_t mm_users;          /* How many users with user space? */  
  6.     atomic_t mm_count;          /* How many references to "struct mm_struct" (users count as 1) */  
  7.     int map_count;              /* number of VMAs */  
  8.     ...  
  9.     //描述用户空间的段分布:数据段,代码段,堆栈段  
  10.     unsigned long start_code, end_code, start_data, end_data;  
  11.     unsigned long start_brk, brk, start_stack;  
  12.     unsigned long arg_start, arg_end, env_start, env_end;  
  13.     unsigned long rss, total_vm, locked_vm;  
  14.     ...  
  15. };  
以上结构描述了进程的用户空间的结构,其中
pgd_t    是该进程用户空间地址映射到物理地址时使用
vm_area_struct 是进程用户空间已映射到物理空间的虚拟地址区间,mmap是该空间区块组成的链表。

虚拟空间的空洞:虚拟空间还未被映射的区块(即没有被使用),那么就没有vm_area_struct结构

2.2 内存区间

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * This struct defines a memory VMM memory area. There is one of these 
  3.  * per VM-area/task.  A VM area is any part of the process virtual memory 
  4.  * space that has a special rule for the page-fault handlers (ie a shared 
  5.  * library, the executable area etc). 
  6.  */  
  7. struct vm_area_struct {  
  8.     struct mm_struct * vm_mm;   /* VM area parameters */  
  9.     unsigned long vm_start;     //虚拟空间起始地址  
  10.     unsigned long vm_end;       //终止地址  
  11.   
  12.     /* linked list of VM areas per task, sorted by address */  
  13.     struct vm_area_struct *vm_next;  
  14.       
  15.     //该区间的权限及标志  
  16.     pgprot_t vm_page_prot;  
  17.     unsigned long vm_flags;  
  18.       
  19.     //一些vm_area 的链接  
  20.     ...  
  21.     struct vm_operations_struct * vm_ops;  
  22.     unsigned long vm_pgoff;     /* offset in PAGE_SIZE units, *not* PAGE_CACHE_SIZE */  
  23.     struct file * vm_file;      //用于将磁盘文件映射至用户空间  
  24.     ...  
  25. };  

虚拟空间区间的描述中:
vm_start/vm_end    为该区块的起始和结束地址
vm_file    是在文件映射中使用到,即常用的mmap(fd,...)函数,简单说即将虚拟空间映射至文件在内核的缓冲区,那么这时候访问该虚拟空间将有别于pgd的映射。
vm_operations_struct 为本虚拟区间的操作,其中的nopage函数指针是处理内存缺页而使用的。对于通用的内存映射,该缺页处理函数为do_no_page()将虚拟地址映射到物理地址(匿名映射):分配物理页& 设置pgd & pte。
而对于mmap操作相关的虚拟地址,其缺页处理函数将和文件系统的缺页函数相关,filemap_nopage(),通过文件系统的缺页从磁盘将相关文件块加载如内核缓冲区.

 

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. struct vm_operations_struct {  
  2.     void (*open)(struct vm_area_struct * area);  
  3.     void (*close)(struct vm_area_struct * area);  
  4.     struct page * (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);     //缺页操作  
  5. };  


3.系统物理地址的组织

内核将物理地址按页来组织,struct page描述系统的物理页的信息,但是页的数据内容是不在该结构中的。系统有全局数据 struct page mem_map[],用于记录每个物理页。
页面大小为4kb,在源码中用体现为(PAGE_SHIFT = 12)

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. /* 
  2.  * Try to keep the most commonly accessed fields in single cache lines 
  3.  * here (16 bytes or greater).  This ordering should be particularly 
  4.  * beneficial on 32-bit processors. 
  5.  * 
  6.  * The first line is data used in page cache lookup, the second line 
  7.  * is used for linear searches (eg. clock algorithm scans).  
  8.  */  
  9. typedef struct page {  
  10.     struct list_head list;  
  11.     struct address_space *mapping;  
  12.     unsigned long index;  
  13.     struct page *next_hash;  
  14.     atomic_t count;  
  15.     unsigned long flags;    /* atomic flags, some possibly updated asynchronously */  
  16.     struct list_head lru;  
  17.     unsigned long age;  
  18.     wait_queue_head_t wait;  
  19.     struct page **pprev_hash;  
  20.     struct buffer_head * buffers;  
  21.     void *virtual; /* non-NULL if kmapped */  
  22.     struct zone_struct *zone;  
  23. } mem_map_t;  

 

struct page是用于描述一个物理页面,该结构仅仅是作为描述,也就是说该页面的4kb数据时存储于某个连续的4kb的物理空间(由MMU决定,具体见下文)。其中:
lru    页面缓冲的调度策略(最少使用优先)

题外话:
page也可以用于文件缓冲,相关参数及作用:
buffer_head    是和设备文件相关的操作,例如在文件系统中,file的一个page有4个块,这些块就存储于buffer_head链表指定的内存中。
index 在文件系统中是用于file缓冲的页号。

 

 

3.1 用户空间页面目录(映射关系)

 

进程的虚拟空间描述中,pgd是用于页式存储的映射使用。当内核发生进程切换时,将新进程的pgd载入CR3寄存器,CPU中的MMU单元依据CR3寄存器进行页面映射。

pgd,pmd和pte可以看做是数组,为进程的地址空间到物理空间实现映射。其中虚拟地址的高位地址决定pgd,中间段地址决定pmd,而低位地址决定pte,pte是“page table entry”。
最终定位的pte中存放的即为对应物理页面的指针。
[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. typedef struct { unsigned long pte; } pte_t;  
  2. typedef struct { unsigned long pmd; } pmd_t;  
  3. typedef struct { unsigned long pgd; } pgd_t;  
  4. typedef struct { unsigned long pgprot; } pgprot_t;  //操作标志  


3.2用户空间的映射:

1. 用户空间的虚拟地址vaddr通过MMU(pgd,pmd,pte)找到对应的页表项x(即为物理地址)
2. 页表项x的高20位是物理也好,物理页号index = x >> PAGE_SHIFT, 同理,index后面补上12个0就是物理页表的首地址。
3. 通过物理页号,我们可以再内核中找到该物理页的描述的指针mem_map[index],当然这个指针是虚拟地址,page结构见上文。


3.3内核空间虚拟地址的映射:

内核空间与物理地址之间有直接的映射关系,而不需要向用户空间那样通过mmu(pgd)。系统空间映射(3G开始)到物理空间0G起始:
例如:
系统内核映像载入的虚拟地址为3G+1M的起始地址,那么对应的物理地址为1M。
紧接着分配在3G+2M开始分配了8M的虚拟地址(物理地址为2-9M)用于PDG
之后预留了16M空间用DMA于存储。
而全局的page结构的mem_page[]数组是在0xc1000000开始的。
所以内核空间虚拟地址到物理地址的转换为:

 

[cpp]  view plain  copy
 
 在CODE上查看代码片派生到我的代码片
  1. PAGE_OFFSET = 3GB  
  2. vitr_to_phys(kadd)  
  3.   return vadd - PAGE_OFFSET  
  4.     
  5. 内核空间的虚拟地址vaddr是通过如下方式找到它对应物理地址的page结构:  
  6. vitr_to_page(vadd)  
  7.     index = virt_to_phys(kadd) >> PAGE_SHIFT  
  8.     return mem_map[index]  

4. 相关数据结构关系图

说明:

1. 黑色+红色 箭头展示了虚拟地址空间到物理空间的映射关系

2. 蓝色箭头涉涉及到文件的映射操作mmap(),相比匿名映射,文件映射多了文件层的磁盘IO。

 

相关文章:

  • nginx建https站实验
  • Flex布局到底解决了什么问题
  • VB之SendKeys键盘模拟
  • win 下 apache 虚拟主机配置方式
  • php libevent 详解与使用
  • Weex技术峰会精华集锦:揭秘火爆Github排行版的跨平台移动开发工具背后技术
  • 用js使得输入框input只能输入数字
  • /usr/bin/env: node: No such file or directory
  • 感觉世界变化太快...
  • Effective C++ iterm4
  • 用虚拟 router 连通 subnet - 每天5分钟玩转 OpenStack(141)
  • ECMAScript 6 入门
  • 指令周期 机器周期 状态周期 振荡时钟周期(时钟周期)(转)
  • 关于java的print()
  • Laravel / Lumen 实践总结
  • 【Amaple教程】5. 插件
  • iOS编译提示和导航提示
  • Linux后台研发超实用命令总结
  • node入门
  • orm2 中文文档 3.1 模型属性
  • php ci框架整合银盛支付
  • python docx文档转html页面
  • python 学习笔记 - Queue Pipes,进程间通讯
  • Rancher-k8s加速安装文档
  • Spring声明式事务管理之一:五大属性分析
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 包装类对象
  • 从0实现一个tiny react(三)生命周期
  • 分布式熔断降级平台aegis
  • 服务器从安装到部署全过程(二)
  • 简单易用的leetcode开发测试工具(npm)
  • 浏览器缓存机制分析
  • 如何编写一个可升级的智能合约
  • 微信支付JSAPI,实测!终极方案
  • ​linux启动进程的方式
  • ​力扣解法汇总1802. 有界数组中指定下标处的最大值
  • #define,static,const,三种常量的区别
  • #前后端分离# 头条发布系统
  • (0)Nginx 功能特性
  • (16)UiBot:智能化软件机器人(以头歌抓取课程数据为例)
  • (2)Java 简介
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • (差分)胡桃爱原石
  • (翻译)terry crowley: 写给程序员
  • (转)Android学习笔记 --- android任务栈和启动模式
  • (轉貼) VS2005 快捷键 (初級) (.NET) (Visual Studio)
  • 、写入Shellcode到注册表上线
  • .NET CF命令行调试器MDbg入门(四) Attaching to Processes
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • [ vulhub漏洞复现篇 ] JBOSS AS 5.x/6.x反序列化远程代码执行漏洞CVE-2017-12149
  • [20190416]完善shared latch测试脚本2.txt
  • [BUAA软工]第一次博客作业---阅读《构建之法》
  • [BZOJ4016][FJOI2014]最短路径树问题
  • [BZOJ5250][九省联考2018]秘密袭击(DP)
  • [C#]C# OpenVINO部署yolov8图像分类模型