目录
一、虚拟地址和虚拟内存
物理内存对于CPU而言就相当于一个字节数组,CPU通过数组下标的方式访问物理内存,比如内存大小是4GB,则对应的数组下标范围是0x0000 0000-0xffff ffff, 这个范围就是物理地址空间,其中的0x00001000就是物理地址。虚拟地址与物理地址形式上一样,不过虚拟地址属于虚拟地址空间,虚拟地址空间是相当于单个进程的,不同进程间虚拟地址空间完全隔离,虚拟地址空间通过硬件上的内存管理单元(MMU)和操作系统映射至物理内存空间。虚拟地址空间按照程序本身的需要可能大于物理地址空间,亦或者当前剩余可用内存不能满足应用程序的需要,这时需要把部分硬盘空间当做内存来使用,这部分当做内存使用的硬盘空间叫做虚拟内存,Linux上即Swap分区。
虚拟地址到物理地址的转换需要MMU和操作系统相互配合,因为这一翻译过程的存在,不同进程间的虚拟地址空间实现完全隔离,即A进程完全不能访问或者修改B进程的数据;同时虚拟地址空间完全摆脱了物理内存的限制,实现两者间的解耦,可以不用考虑物理内存的大小和分配,编译时可以在内存只有目标程序使用且完全满足内存需求的理想前提下提前做好内存的分配,即编译时就确认某个变量或者函数的内存地址,如果是运行时才确认某个变量或者函数的内存地址则需要比较耗时的重定位操作而影响程序性能。
二、逻辑地址与内存分段管理
内存分段机制是8086 CPU为了解决数据总线与地址总线宽度不一致问题,实现16位寄存器能够顺利访问20位地址空间而引入的,20位地址空间对应1Mb的内存,16位地址空间对应64kb的内存,8086CPU将1Mb内存分成16个段,每个段64kb,将原来写死的物理地址拆分成段基地址和段内偏移量,这两个组成逻辑地址,然后增加保存段基地址的段寄存器,段基地址通过段寄存器传递,段内偏移量通过16位数据总线传递,对应的物理地址由段基地址*16+段内偏移地址算出。这种早期的CPU寻址模式成为实模式,因为对对段对应的地址空间缺乏保护,目前只在CPU被复位或者加电的时候使用,当操作系统在此模式下完成必要的初始化就转换到保护模式(IA-32模式)。
保护模式下引入了段描述符的概念,每一个分段都有对应的段描述符和段选择符,段描述符记录了段的基地址、类型和CPU特权级等属性,通过段描述符来实现对对应段的地址空间的权限控制,段描述符保存在全局段描述符表(GDT)和局部段描述符表(LDT),GDT的物理地址保存在gdtr控制寄存器中,LDT的物理地址保存在ldtr控制寄存器中;段选择符标识段描述符在GDT或者LDT中的位置,段选择符保存在专用的段寄存器,如cs 代码段寄存器,ds数据段寄存器,ss栈段寄存器,每个段寄存器有一个对应的不能被代码改写的寄存器用来保存对应的段描述符,当段选择符被装入段寄存器时,CPU自动将对应的段描述符自动装入对应的非编程寄存器,从而实现快速访问段描述符。多核处理器每个CPU都有对应的GDT,由内核创建并维护,LDT和进程包含的内存分段对应的段选择符由进程创建并维护。早期逻辑地址通过分段单元直接转换成物理地址,后面为了解决内存共享,内存碎片等问题而引入了内存分页机制,在逻辑地址和物理地址中间加了一个线性地址,即虚拟地址。将逻辑地址转换成虚拟地址(又称线性地址)由分段单元完成,转换流程如下图:
内存分段是指为二进制可执行文件中的代码段,数据段等segment指定基地址即对应的段描述符从而将其加载到不同的虚拟地址空间范围内,因为进程维护的段选择符较多,且需要频繁切换段选择符,操作系统内存管理实现会相对复杂。Linux采用的替代方案是在二进制可执行文件加载的过程中由加载器按照规则将各segment加载到不同的虚拟地址空间,同时Linux为了规避这种内存分段机制,所有的用户代码共享用户代码段和用户数据段,所有内核代码共享内核代码段和内核数据段,只在进程在用户态和内核态之间切换时才更改段寄存器,从而减少段选择符的频繁切换,并且四个段的起始地址都是0,最大地址是0xfffff(32位下),即逻辑地址等于了虚拟地址,无论使用哪个段描述符对应的虚拟地址都不变,具体而言Linux没有刻意将可执行文件中的代码段放到用户态代码段,数据段放到用户态数据段,CPU读取代码指令或者数据时无论从用户态代码段或者用户态数据段结果都一样,从而极大简化了内存管理。
由于其他的RISC架构的CPU对内存分段支持很有限,Linux为了能够兼容其他架构的CPU和简化内存管理而基本放弃了内存分段而选择了内存分页模式,现代的x86 64位CPU本身也是最大限度的削弱了对内存分段的支持而强化了对内存分页模式的支持。
参考: 总结一下linux中的分段机制
三、内存分页管理和TLB
虚拟地址到物理地址的转换由分页单元完成,通过内存分页管理可以将同一个虚拟地址映射到不同的物理地址上,可以保证进程的虚拟地址空间不受物理内存的地址空间限制,可以限制对非法虚拟地址的访问,可以实现对公共代码的内存共享,以页为单位管理内存也可以大大减少物理内存碎片提供内存的使用效率。所谓的页是指一组连续的虚拟地址,32位下通常是4k