看了就会的Linux虚拟地址和页表分析

1.物理内存
物理内存是由系统实际提供的硬件处理单元,所有的程序指令和数据必须装人内存才能执行。
内存中所有的存储单元从0开始依次编号,这个编号称为这个存储单元的内存地址或物理地址。CPU通过物理地址找到相应的存储单元中存放的指令或数据。内存的地址空间是一维的,它的大小受到实际存储单元的限制,存储单元最大的内存地址加1称为内存空间大小或物理地址空间大小。如下图所示,内存地址编号0000000H~003FFFFFH(十六进制)构成了一个4MB大小的物理存储空间。
在这里插入图片描述
2.虚拟存储空间
我们将进程中的目标代码、数据等的虚拟地址组成的虚拟空间称为虚拟存储空间(virtual memory)。虚拟存储空间不考虑物理内存的大小和信息存放的实际位置,只规定每个进程中互相关连的信息的相对位置。与实际物理内存只有一个(单机系统中)且被所有进程共享不一-样,每个进程都拥有自已的虚拟存储空间,且虚拟存储空间的容量是由计算机的地址结构和寻址方式确定的。

3.Linux存储管理
Linux系统采用了虚拟的内存管理机制,即交换和请求分页存储管理技术(为什么采用此技术,这篇博客可以参考,通俗易懂,虚拟内存(页表,换页))。这样,当进程运行时,不必把整个进程的映像都放在内存中,而只需在内存中保留当前用到的那一部分页面。当进程访问到某些尚未装人内存的页时,就由核心把这些页装人内存。这种策略使进程的虚拟地址空间映射到内存的物理空间时具有更大的灵活性,通常允许进程的大小可大于可用的内存的总量,并允许更多的进程同时在内存中运行。

3.1 地址空间
内核使用mm_struct结构体来表示进程的地址空间,改结构包含了进程地址空间有关全部信息。mm_struct结构体首地址在任务结构体task_struct成员项中。

3.2 进程的虚拟区域
一个虚拟区域是虚拟内存空间中一个连续的区域,在这个区域中的信息具有相同的操作和访问性。每个虚存区域用一个vm_area_struct结构体描述
下面放一张进程虚拟空间的结构图
说明:网上没有找到合适的,正好书上这个不错拍下来放在这。
在这里插入图片描述

3.3虚拟空间的映射和虚存区域的建立
在虚拟存储技术中.用户的代码和数据(可执行映像)等并不是完整地装人物理内存,而是全部映射到虚拟内存空间。在进程需要访问内存时,在虚拟内存中“找到”要访问的程序代码和数据等,系统再把虚拟空间的地址转换成物理内存的物理地址。

Linux使用do_ mmap()函数完成可执行映像向虚存区域的映射,由它建立有关的虚存区域。

unsigned long do_mmap(struct file * file, unsigned long addr,

unsigned long len, unsigned long prot,unsigned long flags,

 unsigned long off)

addr是虚存区域在虚拟内存空间的开始地址: len 是这个虚存区域的长度; file 是指向该文件结构体的指针: off是相对于文件起始位置的偏移量;若file 为NULL,称为匿名映射(anonymous mapping); prot指定了虚存区域的访问特性: flag 指定了虚存区域的属性。

4.虚拟地址映射关系及页表
下面先举一个一级页表的例子,若此时机器为32位,内存大小为4G。
假设每页分为4k,4g就有2 ^ 20个4k,所以一共有2 ^ 20个页号
2 ^ 12 可以表示一个4k = 4096 B的空间
由页号通过页表,再通过偏移量就可算出物理地址
在这里插入图片描述
说明:许多博客和书籍都将页表项简单的划分为 页号和块号,这样理解是有问题的。页表项包含物理地址最高20位的字段和12位标记位。由于每一个页有4KB的容量,它的物理地址必须是4096的倍数,因此物理地址的最低12位总为0,在内存中找到对应的页后,便可根据虚拟地址的12位偏移量便可得出其在当前页的偏移,也就得出了真正的物理地址。
在这里插入图片描述

不过,这样还是挺占内存,因为一个页表项为4B,那么2 ^ 20 个页表项就可达到4M,所以linux采用了更好的办法

5.Linux最初的两级页表机制:

两级分页机制将32位的虚拟空间分成三段,低十二位表示页内偏移,高20分成两段分别表示两级页表的偏移。

PGD(Page Global Directory): 最高10位,全局页目录表索引

PTE(Page Table Entry):中间10位,页表入口索引

当在进行地址转换时,结合在CR3寄存器中存放的页目录(page directory, PGD)的这一页的物理地址,再加上从虚拟地址中抽出高10位叫做页目录表项(内核也称这为pgd)的部分作为偏移, 即定位到可以描述该地址的pgd;从该pgd中可以获取可以描述该地址的页表的物理地址,再加上从虚拟地址中抽取中间10位作为偏移, 即定位到可以描述该地址的pte;在这个pte中即可获取该地址对应的页的物理地址, 加上从虚拟地址中抽取的最后12位,即形成该页的页内偏移, 即可最终完成从虚拟地址到物理地址的转换。从上述过程中,可以看出,对虚拟地址的分级解析过程,实际上就是不断深入页表层次,逐渐定位到最终地址的过程,所以这一过程被叫做page talbe walk。

6.Linux的三级页表机制:

当X86引入物理地址扩展(Pisycal Addrress Extension, PAE)后,可以支持大于4G的物理内存(36位),但虚拟地址依然是32位,原先的页表项不适用,它实际多4 bytes被扩充到8 bytes,这意味着,每一页现在能存放的pte数目从1024变成512了(4k/8)。相应地,页表层级发生了变化,Linus新增加了一个层级,叫做页中间目录(page middle directory, PMD), 变成:

字段 描述 位数

cr3 指向一个PDPT crs寄存器存储

PGD 指向PDPT中4个项中的一个 位31~30

PMD 指向页目录中512项中的一个 位29~21

PTE 指向页表中512项中的一个 位20~12

page offset 4KB页中的偏移 位11~0

现在就同时存在2级页表和3级页表,在代码管理上肯定不方便。巧妙的是,Linux采取了一种抽象方法:所有架构全部使用3级页表: 即PGD -> PMD -> PTE。那只使用2级页表(如非PAE的X86)怎么办?

办法是针对使用2级页表的架构,把PMD抽象掉,即虚设一个PMD表项。这样在page table walk过程中,PGD本直接指向PTE的,现在不了,指向一个虚拟的PMD,然后再由PMD指向PTE。这种抽象保持了代码结构的统一。

7.Linux的四级页表机制:

硬件在发展,3级页表很快又捉襟见肘了,原因是64位CPU出现了, 比如X86_64, 它的硬件是实实在在支持4级页表的。它支持48位的虚拟地址空间。

推荐两篇文章,写得不错,笔者这在收获颇多。
深入理解计算机系统-之-内存寻址(五)–页式存储管理
Linux分页机制之概述–Linux内存管理(六)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值