一、内存地址
- 逻辑地址:段地址+偏移量
- 虚拟地址(VA):32位无符号整数,可表示4G内存控件,例如0x00000000
- 物理地址:芯片级内存单元寻址,直接映射在硬件上的地址
- 逻辑地址和虚拟地址的区别:进程所使用的虚拟内存中的地址是虚拟地址,而逻辑地址需要包换段偏移。例如0x12345678为虚拟地址,[所对应的段地址:0x12345678]为虚拟地址
二、硬件的分段
- 从80286开始,地址转换有实模式和保护模式两种(X86中)
- 段地址称为段选择符(段选择子),实际上不是一个地址,是一个16位长的字段
- cs段寄存器存储了当前指令所在的段,其中有两位表示特权级别(CPL),0和3(即0环和3环),分别为内核态和用户态
- 段描述符存放于全局描述符表(GDT)或局部描述符表(LDT)中,8字节,用于描述段特征
- GDT的地址和大小存在gdtr寄存器中,LDT的地址和大小存在ldtr寄存器中
- [段描述符字段]Base:段在内存中首字节的线性地址(基地址),S:0表示系统段,1表示普通段,DPL:特权级,0表示只有内核态才能访问,3表示内核态和用户态都能访问,D或者B:表示这是代码段,还是数据段,G:为0以字节为单位,否则以4096字节为单位

- 不同种类的段描述符结构完全相同,只是个字段值不同
- 当一个段选择符被放入段寄存器,对应的段描述符就会被放入一种非编程寄存器中
- 进行逻辑地址转换时只有段地址寄存器内容改变时才会访问GDT或LDT,否则直接在寄存器中取值即可
- [段选择符字段]index:段描述符在GDT或LDT中的索引(类似数组下标) ,TI:描述段描述符在GDT中还是LDT中 ,RPL:请求者特权

- 段描述符的逻辑地址由index字段值*8(8字节长度)+GDT(或LDT)的基地址(在gdtr或ldtr中)得到
- GDT中第一项总为0,所以0x0地址是一个无效地址,会报异常
- GDT中可保存的段描述符最大数量为2^13-1
- 将逻辑地址转换为虚拟地址的过程会首先根据上述过程找到段描述符地址,再根据段描述符地址中的base字段找到段首地址(真正的段地址),再与偏移地址相加即得到了虚拟地址
三、Linux的分段
- linux中运行在内核态和用户态的代码分别使用一对段描述符来分别对代码和数据进行寻址,其分别是内核代码段、内核数据段、用户代码段、用户数据段
- 上述的四个段分别由宏USER_CS、__USER_DS、KERNEL_CS、__KERNEL_DS定义
- 因为在linux下整个用户进程或整个数据进程公用相同的段,且所有的段都从0x0开始,所以可以认为在linux下虚拟地址和逻辑地址是相同的
- linux这么做是为了实现硬件兼容性,因为有些cpu架构不支持分段机制,所以linux弱化了这个机制
- linux中每个cpu对应拥有一个GDT,所以单处理器系统只拥有一个GDT
- 每一个GDT中包含18个描述符和14个未使用的或保留的描述符
- 每个处理器有一个任务状态段(TSS),一个缺省的局部描述符表的段,三个局部线程存储段(TSL),三个高级电源管理相关的段,五个与支持即插即用功能的BIOS服务程序相关的段,一个被用来处理双重异常的特殊TSS段
- LDT大多数用户态程序不使用,且每个任务最多只能拥有一个LDT
四、硬件的分页
- 页是线性地址的固定长度的分组,被映射到物理地址上
- 页框(物理页)是将RAM分成固定长度的组,每一个页框包含一个页
- 页框用于承载页,是主存的一部分,页是一个数据块,可以存放在任何页框中
- 页表把线性地址映射成物理地址
- 32位的线性地址由目录(高10位)、页表(中间10位)、偏移量(低12位)组成(我记得现在的CPU已经不是二级寻址了)
- cr3寄存器中存放着当前正在使用的页目录表的地址,由这个地址加线性地址中的目录字段找到页表,再根据页表字段找到对应的页,最后根据偏移找到物理地址。因为由12位表示偏移量,所以每一页的大小有4k
- 页目录表项和页表项结构相同
- 从奔腾模型开始,引入了一种页大小为4m的扩展分页机制,直接在页目录中寻找对应的4m的页,此时线性地址的高10位是页目录字段,后22位是偏移量
- 将常规分页的表项中的pagesize字段置1即为扩展分页
- 页的存储权限只有只读与可读写两种,由read/write字段控制,段则有读、写、执行三种(忘了为什么页只有读写没有执行了。。。)
- 页的权限只有两种,由user/supervisor字段表示,该值为0时,在Linux下只有内核态(CPL小于3)才可以进行寻址,若为1任何状态都可以寻址
- 假设一个进程的线性地址为0x20000000-0x2003ffff,页目录字段相同,则该进程的页目录表种只有一项,其余项都为0,页表项从0-0x3f,所以页表种只有64项,其余项都为0,最后根据剩下的偏移地址找到物理地址(所以每个进程都有一个页目录表)
- 对于64位CPU来说,其采用2级以上的寻址方式,这样做是因为加入仍旧使用2级寻址,页目录表和页表中表项会十分多
- 引入硬件高速缓存是为了消除cpu和主存的速度差距
- 行是80x86cpu引入的新单位,其是由几十个连续的字节组成
- cr0寄存器中的cd标值为用来判断是否启用告诉硬件缓存,nw标志位用来确定使用回写策略还是通写策略
- 转换后援缓冲器(TLS)用于存放线性地址转换后的物理地址,用于加快转换速度
五、Linux中的分页
- linux中采用四级分页模型,但该模型同时适用于32位和64位CPU。当CPU为32位时,linux使线性地址中间的两级页表值全为0来消除作用
- 32位时,如果启用了PAE则为3级分页,64位时的分页情况取决于CPU讲线性地址划分成几个部分
- 在初始化阶段,内核必须建立一个映射来指定那些物理地址对内核可用,那些不可用(因为含有bios数据或映射硬件设备IO的共享内存)
- 不可用的物理内存或含有内核代码和已经初始化数据结构的页框不能被动态分配到内存上或交换到磁盘上
- 主存中的第一个mb的空间是不能被内核使用的,其给bios使用,所以内核一般从0x00100000处(第二个mb)开始安装




