x86架构通用寄存器(32位的x86架构和64位的x86_64架构)

x86_64架构由amd公司推出,因此又叫amd64架构,64位架构是基于32位架构扩展的

32位架构的x86处理器具备8个32位的通用寄存器,可通过名称来引用这8个寄存器,分别为EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI

EAX的低16位可以被单独使用,引用名称叫AX(高位是左边位,低位是右边位),AX又可以被分为高8位的AH和低8位的AL

实质上EAX,ECX,EDX,EBX都可以被拆开使用,ECX的低16位叫CX,CX又可以被分为高8位的CH和低8位的CL

EDC的低16位叫DX,DX又可以被分为高8位的DH和低8位的DL,EBX的低16为叫BX,BX又可以被分为高8位的BH和低8位的BL

ESP,EBP,ESI,EDI的低16位也可以被单独使用,但是没有8位的,这低16位名称分别是SP,BP,SI,DI

EAX寄存器被乘法和除法指令自动调用,因此又叫累加寄存器

ECX被LOOP(循环)指令调用为循环计数器

ESP被用于寻址栈上的数据,ESP始终指向栈顶,因此又叫栈指针寄存器

ESI和EDI又叫变址寄存器,变址寄存器引用的是内存地址,ESI指向内存源地址,EDI指向目的地址

EBP叫帧指针寄存器,被用来引用栈上的函数参数和局部变量

除了通用寄存器还有EFLAGS标志寄存器,EIP指针寄存器(这个非常重要,因为其引用的是下一条要被指向的指令的地址,注意:并不能直接通过名称来说调用,只能通过CALL之类的间接修改)等等

64位架构(通用寄存器为16个,并且是64位的,而且每个都可以低8位,16位,32位单独使用)

EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI的64位是R开头的,其他和32位架构是一样的,64位架构的内存地址也是64位的

x86汇编指令(通常是由一个操作码(opcode)和0到多个的操作数(operand)组成)

整数加减指令(ADD指令(有2个操作数,分别是目的操作数和源操作数,ADD指向将这2个操作数的值相加,将结果存放在源操作数中,源操作数可以是寄存器,内存,目的操作数要满足可写条件,因此也可以是寄存器,内存,但是不能同为内存))和SUB指令(和ADD指令一样,但是是将结果存放在目的操作数中))

ADD指令:ADD EAX,32 (将EAX寄存器的值加上32,并且将结果存放回EAX寄存器中)

SUB指令:SUB ESP,32 (将ESP寄存器的值减去32,并且将值存放回EAX寄存器中)

数据传输指令(x86架构有多个数据传输指令,这里是MOV指令)

MOV指令用于寄存器之间和寄存器和内存之间传输数据使用,MOV指令将源操作数复制到目的操作数中,例如MOV EDX, 666 (将数值666存储在EDX寄存器中)

x86架构内存寻址:displacement(位移,可以在指令中直接得到内存的偏移量,也就是位移,这个位移表示距离操作数的直接偏移量),base(基址,内存地址存储在通用寄存器中),index(索引,注意ESP寄存器不能用于索引),scale(比例因子,用于索引相乘,是固定值,可取值1,2,4,8)

内存最复杂的地址计算公式:base+(index*scale)+displacement

base和index,displacement都可以随意组合,也可以不存在,如果不使用index,就不需要使用scale了,scale只为index服务,index和scale被用于寻找数组地址和多维数组

入栈和出栈指令(PUSH和POP指令)

PUSH指令只有一个操作数,就是需要入栈的源操作数,这个指令可以将ESP寄存器向下移动一个位,并且将源操作数复制到ESP寄存器指向的内存处,例如:PUSH EAX

POP指令也是只有一个操作数,就是用来接收数据的目的操作数,POP指向会将ESP寄存器指向的内存处的值复制到目的操作数中,并且将ESP寄存器向上移动一个位置,例如:POP EAX

分支跳转指令(JMP指令)

JMP指令只有一个操作数,这个操作数可以是内存,寄存器或者立即数,通过这个操作数来给出需要跳转的目的地址,例如 JMP EAX

过程调用指令(CALL指令)

高级语言的函数在汇编叫过程,CALL指令只有一个操作数,是过程的起始地址,例如 CALL EAX

分支跳转指令和过程调用指令的区别是,分支跳转指令不会记录返回地址,这个返回地址是CALL指令之后的下一条指令的地址,CALL指令会将返回地址入栈,然后跳转到目的地址执行

子过程执行完成通过RET指令返回,RET指令会在栈上弹出返回地址,并且跳转到该返回地址上继续执行


内存分页机制

线性地址

在内存分页模式中,应用使用的地址就叫线性地址,由MMU(menorymanagement unit)基于页表来映射转换为物理地址

在内存分页模式未出现之前,应用是直接访问物理内存的,应用具备读写全部物理内存的权限,因此可能会覆盖其他应用的数据,而80386架构出现,出现了保护模式,使用内存分页来通过特权级和进程地址空间来进行隔离

进程地址空间隔离是通过进程独立性页表来完成的,每个进程实现的地址空间是不同的,避免影响到其他进程

80386两级页表

80386架构的线性地址为32位,因此可寻4GB大小的内存空间(4096),地址总线也是32位,因此也是只能寻找4gb大小的物理内存,而且分页机制也将每个物理内存的页面的大小设为4096字节

一个页面大小为4096字节,地址总线为32位,因此一个页面可存储1024个物理页面地址,80386页表的第一页面是目录页面,物理内存地址存储在CR3寄存器中,可通过该目录页面来查找第二页面的1024个物理页面地址

MMU将32位的线性地址,低12位是页内偏移,然后的低10位是页表的索引,最后的高10位是页面目录索引,页内偏移的取值范围为0到4095,页表索引和页面目录索引的取值范围为0到1023

80386线性地址转换到物理地址的过程:先从CR3寄存器中获取页目录的物理地址,然后选择一个页表,在到页表索引中,找到页面的物理地址,最后通过页内偏移量来得到实质上的物理地址

PAE三级页表

80386架构的每个进程可使用4gb的线性地址空间,但是操作系统会将4gb的地址空间划分成用户空间和内核空间,为了解决内存空间不够使用,英特尔公司推出了物理地址扩展技术(PAE,PhysicalAddressExtension)

PAE将地址总线扩展到36位,因此可寻找64gb的物理内存,但是线性地址依然是32位的,为了解决32位线性地址支持36位的物理地址映射,MMU页表映射机制进行了调整,一个页面只能存储512个地址

PAE的32位线性地址是,高2位是页目录指针索引,后面9位是页目录索引,再后9位是页表索引,最后12位是页内偏移

x64四级页表

因为PAE技术并没有扩展线性空间,32位的地址宽度不够使用了,AMD公司基于x86架构扩展而出的x64架构,x64架构的寄存器宽度是64位的,但是线性地址只使用了48位,但是也足够了,因为可以寻高达256TB的内存空间地址,具体可寻多少物理内存空间,取决于地址总线的宽度

x64架构,在PAE的基础上扩展了页表为4级,而且每个页面的大小是4096字节,高9位是页目录指针表,后9位是页目录指针,再后9位是页目录,然后9位是页表。最后12位是页内偏移量

虚拟内存

进程是以内存页面为单位向操作系统申请内存的,现代操作系统中会对申请的内存空间进行记录,并不会马上分配,而是等到该进程真正访问该内存空间是才会分配物理页面并且进行映射,然后恢复中断程序,如果进程访问了没有映射的内存空间,会被操作系统进行page fault处理,操作系统通过page failt handle进行检查内存空间分配记录

物理空间不够分配时,操作系统可以将少使用的物理页面写入到磁盘交换分区(Swap分区)中,将空出来的页面给需要的进程使用,注意:当在磁盘交换分区中内存页面被访问了,也会触发page fault处理,操作系统通过page failt handle来将磁盘交换分区的内存页面加载回内存中

虚拟内存并不是万金油,触发虚拟内存的条件是物理空间不够分配时