P001-分页的推理
2022-11-09 20:58:42 0 举报
IO相关
作者其他创作
大纲/内容
内核态
获取一个4KB的空页(物理)函数Linux0.11-源码
0xffff ffff(32位)
第三章计划如何分段,将逻辑地址映射到线性地址空间。分页机制将会展示如何从线性地址空间映射到物理地址空间而且被用于直接访问内存或者IO设备(IO MAP)。分页的转换机制将会从线性地址转化到物理地址,对于每一次转化,访问线性地址空间都要经过地址访问权限检验后才被允许访问,根据内存类型来判断是否使用缓存。在Intel64位处理器上支持三种不一样的分页模式。在4.1、4.2、4.3、4.4、4.5节中
逻辑地址
分页禁止
0x4000 0abe
使用计算机基础寻址单元的4KB来描述整个内存空间的地址,多级指针设计这也是计算机的精髓所在
如果分页是不启用的,那么线性地址空间就等于物理地址空间。物理地址空间被定义为一段能够在地址总线生成的地址。衍生知识点:CPU控制其它的模块,需要三类总线:1、控制总线:指明读/写2、数据总线:传输数据3、地址总线:指明操作哪一段地址因为多处理器系统通常会定义一个线性空间,这个空间要比物理空间通常要更大。因为要考虑到经济实惠,比如说在Linux0.11版本中物理内存只有16MB,而线性地址空间有4GB,就要将逻辑地址映射到线性地址空间中,然后再从线性地址空间中分页映射到物理地址空间中,那么线性地址空间的处理将会通过分页机制来完成。分页将支持虚拟内存(swap分区)的环境当一个大的线性空间被模拟到一个小的物理地址空间上。衍生知识点:由于频繁的swap,会导致性能变低,比如:Redis、ES的内存抖动,关闭swap分区当使用分页,会将每个段分割为不同的页(大小4KB),最终会存储,要么在物理内存中,要么在磁盘中。操作系统或者执行程序将维护一个页目录表和一组页表来保持整个页的追踪。当一个进程或者任务,尝试去访问一个线性地址空间里的地址时,处理器将会使用页目录和页表去转化线性地址映射为物理地址,然后执行请求操作(读或写)在内存中。如果分页将会被访问,但不在当前物理页中,处理器会触发一个异常(page-fault exception)。操作系统或者执行程序从物理内存或者磁盘中读取页信息,并且将页目录和页表项进行填充,然后执行当前程序。当分页被操作系统或者程序所实现的时候,对执行程序进行透明的交换。
将内存切割规整,一块一块的,也容易置换
描述一个页帧,其中20bit用于描述页帧的基址,其它位用于描述页帧的元数据
二级页表
所以PDT只需要16项就能表示64MB
总共页表的大小是2^20次方个,描述每个页帧
2级页表
4MB
一个好的系统设计,放入的元数据信息越少越好尽量将内存让给应用程序
没有分配二级页表
32位的地址肯定会映射到物理页帧上怎么映射呢?是个算法问题
3级页表
mov 0x4000 0abe,eax
64MB
4.1 分页的模式和控制的位
CR3
4KB
4byte
总结:为什么页表从一到二又到4级呢?精髓:原始的问题就是因为页表内是连续的,项数还多,但是又用不到那么多,浪费内存那么去解决该问题,引入了稀疏存储(使用链表来完成空间不连续的操作)
没有映射页帧
10bit
GPE 异常
内存不规整,不利于管理:内碎片与外碎片
稀疏存储模型,使用链表来完成空间不连续的操作
由于此表需要连续,不管你用不用完所有的项,都必须分配4MB,一个进程根本不可能用完所有的2的20次方个项
20bit
分出20 位来查表作为索引下标推理得出:4MB 内存必须连续
1KB
解决方案:
保护模式分段的弊端:1、内存不规整,造成内存外碎片,若是分段分的内存大了,还会造成内存内碎片2、置换性能低下既然有这些问题就要解决它,那么就需要让内存变规整
偏移量
页表
分出12 位来描述偏移量
1、Linux采用延迟分页、加页: 1、分配虚拟地址,建立页表,页表初始是空的 2、当访问页表时,什么也没有代表没有页帧映射到它,导致缺页异常(page_fault),由于是fault,当处理完handler 后,会重新执行导致缺页异常的指令,其实就是修复再执行(找到物理页,将其分配给该进程,如何分配?建立页表映射) 3、程序怎么找到page_fault的handler函数呢?通过IDT表,CPU预定于了缺页异常的中断号(下标),去找到IDT表项,获取段 选择子,查找GDT表项,获取段基地址+IP的偏移地址,执行handler函数,由于异常类型是fault,所以执行完handler函 数会返回导致fault的那条指令重新执行那么OS的目的就很明显了:系统初始化的时候,在内核态中,搞个page_fault的handler挂到IDT表中,然后在实现page_fault的handler里,找到CR2寄存器里的产生page_fault的线性地址,通过该线性地址,找到页目录表的Entry,然后看是否存在页表,若存在,再找到页表的Entry,然后遍历物理页帧找到一个空的页,然后填写上即可在Linux高版本中比如2.6中,线性地址空间是每个进程独享的4GB的(分段时它的基地址为0,limit为4GB==相当于没有分段),而在低版本中比如0.11中,一个进程分段时是64MB的线性地址空间
总结:MMU处理地址的过程1、从IP寄存器中获取逻辑地址2、再通过段寄存器里的段选择子(GDT的下标)3、查GDT表的表项,映射到线性地址的段基地址4、MMU继续用线性地址切割为10、10、12位5、第一个10位是一级页表的下标,定位一级页表的表项,获取二级页表的基地址6、第二个10位是二级页表的下标,定位二级页表的表项,获取页帧的基地址7、第三个12位是页帧的偏移量,定位到物理地址的某个字节上至此,搞定
总共1024项,能够表示1024个PET
DPL
推理:1、TLB 表项有限2、若分页地址太多将会导致TLB的表项被置换3、所以:我们可以采用大页内存来增加TLB的命中率,从而提高性能(eg:128GB / 4KB = N 非常大 就会导致页帧数多,而TLB的表项有限,会导致TLB的表项疯狂的在置换,所以性能会极度地下降,解决方案:将分页的页帧调大,即将4KB调整为4MB 128GB / 4MB )
1、找到所属页帧(基地址),页帧就是4KB的内存块,那么最大的内存就是4GB,那么最大的页帧项就是(4GB/4KB)==(2^32/2^12)==2^20 项 由于我们需要描述页帧的元数据,所以位数肯定要大于 2^20 => 使用32 位来描述一个页帧,其中 20bit(位) 用于标识 页帧的基址 其它位用于描述页帧的元数据信息,那么这个页帧(32bit)所占大小即为4 byte,而最大的页帧项数为 2的20次方,则需要 4MB 的内存 才能描述完 2的20次方个 页帧,那么这个描述页帧的内存空间有些大啊?2、找到所属页帧的偏移地址,用于找到该地址所指向的那一个byte,由于这个偏移地址用于寻址4KB的大小,那么2的12次方*1byte(内存的基本寻址单元大小)能够表示4KB(2^12)
IDTR
1级页表
一级页表的项数限制多大好呢?二级页表的项数限制多大好呢?由于一级页表要是大了(连续内存)浪费内存,二级页表要是大了(连续内存)也是浪费内存,干脆一样大,对半分查询页表过程:先通过第一个10bit下标找到第一个页表的表项(存储有二级页表的基地址),再通过第二个10bit找到二级页表的表项(存储有最终的页帧基地址),最后第三个12bit是该页帧的偏移量
page fault
总共1024项,能够表示1024个物理页帧
MMU是Memory Management Unit的缩写,中文名是内存管理单元,它是中央处理器(CPU)中用来管理虚拟存储器、物理存储器的控制线路,同时也负责虚拟地址映射为物理地址,以及提供硬件机制的内存访问授权,多用户多进程操作系统TLB(Translation Lookaside Buffer)传输后备缓冲器是一个内存管理单元用于改进虚拟地址到物理地址转换速度的缓存。TLB是一个小的,虚拟寻址的缓存,其中每一行都保存着一个由单个PTE组成的块。如果没有TLB,则每次取数据都需要两次访问内存,即查页表获得物理地址和取数据
用户态
IDT
10
数据结构的妙用:根据不同数据结构的特性,选择处理即可
MMU内存管理单元
根据前面学到的知识,Intel的异常类型分为:Fault、abort、trap,而由于fault 在修复完成后,会重新执行导致fault的指令,所以此异常类型直接命名:page fault推理得出:这种异常只能是错误Fault,因为它可以处理完后,返回原指令继续执行
物理地址
查页表
PET
需要预分配内存
GDT
12bit
base address:指定线性地址空间的起始地址limit:指定从起始地址开始的这块空间的大小限制dpl:特权级访问权限位- 非一致性代码 要求数值上CPL==DPL- 一致性代码 要求数值上CPL>=DPL
Page Entry Table
此乃,大页内存出现的原因
线性地址
保护模式-分段机制
一级页表的大小是2^10次方 * 4byte == 4KB
谁来缓存呢?TLB 缓存当前页表的最近的映射,增加访问速度
一级页表
描述内存:
异常
这种寻址算法就是多叉树衍生出,索引的概念,即为描述数据的数据就是索引,那么MySQL为什么采用B+树呢?为了减少树高,减低查询节点的数量,提高查询性能,仅此而已
与Gate的DPL权限校验通过后自动提权、堆栈切换、保存上下文TSS
链表 + 数组
类比于段寄存器的隐藏部分
增加一个缓存,来保存之前的已经存在的页表映射
将内存规整化,分多大合适?分大了会如何?若分大了但是用的很小,那么会造成内碎片问题分小了又会如何?多分几次,因为第一次的很快用完了,又要分配而且页表项会变多,则意味着元数据信息变多Intel 觉得:分 4KB 最合适,那么此时每次分配的大小都是4KB而我们将物理内存切割为4KB一个单元,称之为 页帧
寻址一个byte
引入二级页表,将连续的内存,打散,大的方面不连续,小的方面仍然是连续的
PDT
MMU单元每次分配页,就需要查表,那么性能太慢
描述磁盘:
权限校验不通过访问超过界限Limit
如果想让3级结构兼容2级结构,可以让2级页表的基地址置为0,即输入的和输出的是一样,当2级页表不存在
总共16MB
总的内存空间是4GB / 4K = 2^20 => 需要20位来描述 二级页表 基地址
由于引入了二级页表,这里的一级页表所占内存远比之前的页表要小
页帧
IP
推理:1、每个进程都有自己的页表2、每个进程都有自己的虚拟地址3、不同进程虚拟地址空间相同4、所以:当切换进程时,TLB缓存需要清空,而线程则不需要清空,因为线程是与当前进程共享内存空间
根据前面学到的知识,Intel处理异常的方式,将与OS联合,OS将fault handler 的入口放在IDT里,此时,如何去学习 page fault 的异常处理机制呢?是不是就是找到OS 的初始化代码的trap_init 处找到page fault 处理函数就行了
由于不使用,所以不需要预先分配内存
Disk
所以Linux0.11版本中进程的页目录表是共享的,而页表是自己的
Page Directory Table
GATE 门
而处理此异常,我们需要填表,填两个:页目录、页表的页帧地址,那么又由于这两个地址是由 线性地址 算出来的(10 + 10 + 12),所以我们需要拿到该线性地址,所以怎么拿呢?Intel CPU 将会把该地址放入 CR2 寄存器中
GDTR
总共二级页表的大小是2^10次方 * 4byte == 4KB
Intel的保护模式
保存磁盘上的缓存信息
查页表的过程
弊端:
一个文件 由多个 1KB block 组成,可以不连续那么怎么寻址它们呢?使用一个1KB block 元数据信息 指定一个block块再用这个block块去读出描述其它块的信息
Linux在高版本中不使用分段机制,但是Intel又必须要使用分段
页目录
64MB/4KB16384
CS
12
剩余20位
收藏
0 条评论
回复 删除
下一页