linux 内存寻址

(持续更新)

相关概念

查看的书籍为 深入linux内核


内存地址

  当使用80x86(32位)微处理器时,一般分为三种不同的地址: 


逻辑地址

 包含在机器语言指令中用来指定一个操作数或一条指令的地址。每一个逻辑地址都由一个段(segment)和偏移量(offset或displacement)组成,偏移量指明了从段开始的地方到实际地址之间的距离。
所有段都从0x00000000开始,只需关注段内偏移即可。而段内偏移的值恰好等于线性地址的值。


线性地址(也称虚拟地址)

线性地址通常用十六进制数字表示,值的范围从0x00000000到0xffffffff。
物理地址
用于内存芯片级内存单元寻址。他们与从微处理器的地址引脚发送到内存总线上的电信号相对应。 

物理地址

用于内存芯片级内存单元寻址。他们与从微处理器的地址引脚发送到内存总线上的电信号相对应。 

页(page)

线性地址被分成以固定长度为单位的组,称为页。页内部连续的线性地址被映射到l连续的物理地址中。内核可以指定一个页的物理地址和其存储权限

页框(page frame)

分页单元把所有的RAM分成固定长度的页框(有时叫做物理页)。

每一个页框包含一个页,也就是说一个页框的长度与一个页的长度一致(目前操作系统应该不是)

页表 (page table)

把线性地址映射到物理地址的数据结构称为页表。

页表内存储页表项,页表项含有页所在页框的物理地址。

在启用分页单元之前必须由内核对页表进行适当的初始化。

地址转换

内存控制单元(MMU)通过一种称为分段单元的硬件电路把一个逻辑地址准换成线性地址;接着,第二个称为分页单元的硬件电路把线性地址转换成一个物理地址。 
这个地方有疑问! 逻辑地址一般等于线性地址,那不同的进程中相同的地址获取到的逻辑地址是一样?还要继续看。后面看了一下,这里是一样的,每个进程都有自己单独的页表,所以不同进程中的相同的逻辑地址转成相同的线性地址,再通过不同的页表找到不同的物理地址。

段选择符

机器语言指令中出现的内存地址,都是逻辑地址。

一个逻辑地址由两部分组成: 段选择符以及偏移量。

段选择符字段

index 指定了放在GDT或LDT中的相对应段描述符的入口。 

TI      :TI (Table Indicator)标志 :指明段描述符实在GDT(TI=0)中或在LDT中 (TI=1)

RPL   :请求者特权级。

段描述符

每个段由一个8字节的段描述符表示,它描述了段的特征。段描述符放在全局描述符表(Global Descriptor Table,GDT)或局部描述符表(Local Descriptor Table, LDT中)。

Linux GDT(全局描述符表)

在单处理器系统中只有一个GDT,而在多处理器系统中每个CPU对应一个GDT。

通常只定义一个GDT,每个进程除了存放在GDT中的段之外如果还需创建附加的段,就有自己的LDT。GDT在主存中的地址和大小存放在gdtr控制寄存器中,当前正被使用的LDT地址和大小放在ldtr控制寄存器中。

段描述符类型

代码段描述符

数据段描述符

任务状态段描述符(TSSD)

局部描述符表描述符(LDTD)

段描述符字段 

Base  : 包含段的首字节的线性地址

.............

.............

分段单元

逻辑地址转换为线性地址,分段单元执行以下操作

1.通过逻辑地址(由段选择符及偏移量组成)得到段选择符;

2.先检查段选择符的TI字段,判断段选择符保存在哪一个描述符表中。TI字段指明描述符是在GDT中(在这种情况下,分段单元从gdtr寄存器中得到GDT的线性基地址)还是在LDT中(在这种情况下,分段单元从ldtr寄存器中得到LDT的线性基地址);

3.从段选择符的index 字段计算段描述符的地址,index字段的值乘以8(一个段描述符的大小),这个结果与gdtr或ldtr寄存器中的内容相加;

4.把逻辑地址的偏移量与段描述符Base字段的值相加就得到了线性地址。

简单说就是,通过逻辑地址找到段选择符,再找到对应的段描述符,最后得到最后的线性地址。

段描述符中有包含段的首地址的线性地址(base)。

常规分页

线性地址结构

32位的线性地址被分成3个域页表 (我的理解:这里怎么分具体要看有几级页表以及页的大小

Diretory(目录)                

        最高10位

Table(页表)          

        中间10位

Offset(偏移量)

        最低12位

这里分为两级页表,线性地址的转换就分两步完成,每一步都基于一种转换表,第一个种叫页目录表,第二种叫页表

个人理解:

如果进程使用全部4G线性地址空间,每个页框(物理页)代表4096个字节(2的12次方);一个页表最多存储1024个页表项(2的10次方),1个页表项含有页所在页框的物理地址,即1个页表项对应4k地址空间,一个页表能找到4M地址空间;1个页目录最多存储1024个页目录项(2的10次方),而一个页目录项指向适当的页表,那么一个页目录项就能找到一个4M地址空间,一个页目录就能找到4G地址空间。

由上图可知,一个页大小为2的10次方,那么内存就以2的十次方分页,知道页表项大小为2字节,

1.一个页表中的页表项数量为 1024(2的10次方) / 2 = 512(2的9次方)个,页号由9位表示,

2.一个页表能表示的空间大小为 512(2的9次方) *  1024(2的10次方) = 512K (2的19次方),

3.一个页目录包含表项的个数 为 总空间大小除以一个页表能表示的空间大小,即

(2的16次方) * (2的10次方) / 512K (2的19次方) = 128 (2的7次方)。

书看到这里有了疑惑点:

书上说,使用这种二级模式的目的是在于减少每个进程页表所需RAM的数量,二级模式通过只为进程实际使用的那些虚拟内存区请求页表来减少容量。

那么问题来了,我只用一级页表为什么不能对实际使用的那些虚拟内存区请求页表?

这里我的理解是为了方便查找,多级页表让查找更快。

硬件高速缓存

当今微处理器时钟频率接近几个Ghz,而动态RAM芯片的存取时间是时钟周期的数百倍。这意味着,当对RAM指令执行时,CPU可能等待很长时间。

x86体系结构引入了一个叫行(line)的新单位。行由几十个连续的字节组成,他们以脉冲突发模式在慢速DRAM和快速的用来实现高速缓存的片商静态RAM(SRAM)之间传送,用来实现高速缓存。高速缓存再被细分为行的子集。高速缓存单元在分页单元和主内存之间。它包含一个硬件高速缓存内存和一个高速缓存控制器。高速缓存内存存放内存中真正的行。

当访问一个RAM存储单元时,CPU从物理地址中提取出子集的索引号并把子集中所有行的标签与物理地址的高几位相比较。如果发现某一个行的标签与这个物理地址的高位相同,则CPU命中一个高速缓存(cache hit);否则,高速缓存没有命中(cache miss)。

当命中一个高速缓存时,高速缓存控制器进行不同的操作,具体取决于存取类型。对于读操作,控制器从高速缓存中选择数据并送到CPU寄存器;不需要访问RAM因而节约了CPU时间。对于写操作,可能采用两个基本策略之一,分别称之为通写和回写。在通写中,控制器既写RAM也写高速缓存行,为了提高写操作的效率关闭高速缓存。回写方式只更新高速缓存行,不改变RAM的内容,提供了更快的功效。当然,回写结束以后,RAM最终必须被更新。只有当CPU执行一条要求刷新高速缓存表项的指令时,或者当一个FLUSH硬件信号产生时(通常在高速缓存不命中之后),高速缓存控制器才把高速缓存写回到RAM中。

转换后援缓冲器(TLB)

除了通用硬件高速缓存以外,还包含了另一个称为转换后援缓冲期或TLB的高速缓存用于加快线性地址的转换。当一个线性地址被第一次使用时,通过慢速访问RAM中的页表计算出相应的物理地址。同时,物理地址被存放在一个TLB表项(TLB entry)中,以便以后对同一个线性地址的引用可以快速得到转换。

当CPU的cr3控制寄存器被修改时,硬件自动使本地TLB中的所有项都无效,这时因为新的一组页表被启用而TLB指向的旧数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值