IA-32体系结构粗浅认识

最近学校开了汇编课,上的是《Intel汇编语言程序设计》(第5版),由于之前为了打ctf,提前看过这本书的前几章,当时白纸一张,对这本书的第二章不是很理解。现在再看这一章,似乎有所体会,记录下来。(这篇博文非常入门,菊苣们可以跳过了。。)

所谓IA-32即Intel Architecture-32,是指从intel 386到奔腾的系列处理器。
IA-32体系结构这本书里只用了一章作了简略的介绍,但是里面还是介绍了许多基本、通用的概念和Intel特有的处理方法,从下面这张图开始,回顾一下处理器和内存互动的基本流程。
IA-32体系结构粗浅认识_第1张图片
CPU不能够直接从硬盘中读取指令和数据来计算,所有在cpu上运转的指令和数据都是来自内存。这里我们暂不关心硬盘中的数据是如何装载入内存以及指令和数据在内存中是怎么分布的(展开东西太多)。先从图中的几个部件开始说起:

  1. 内存
    书中介绍了2种内存:RAM(Random Ascess Memory),ROM(Read-Only Memory)
    RAM(随机存取存储器),它是与CPU交互的主要内存,也就是我们日常生活中说的断电丢数据的“内存”。RAM的“随机存取”体现在它获取内存数据或指令的时间和这条被获取的指令或数据在内存中的位置无关。区别于“读取或写入顺序访问”(Sequential Access)存储设备。(参考:RAM概念)
    ROM是只读存储器,我们熟悉的BIOS程序就是固化在ROM中的控制硬件基本输入输出的程序。ROM是只读内存,断电之后其中的数据不会消失。

  2. CPU
    对于CPU的内部构造这里不想说的太多,只要知道CPU的基本组成部件:时钟、ALU、CU、寄存器。
    时钟是整个计算机运转的“节拍器”,机器指令的最小执行时间就是一个时钟周期,它就像方阵的鼓点,整个方阵都踩着鼓点运转,IA-32这本书中讲到了一些超标量处理的知识,体现的就是intel处理器能够在同一个时钟周期内处理多条指令的不同执行阶段的能力。
    ALU是算数单元,顾名思义就是执行各种算数运算和逻辑运算。
    CU是控制单元,是整个CPU的指挥中心。它由指令寄存器(Instruction Register)、指令译码器(Instruction Decoder)、指令控制器(Operation Controller)组成。CU协调各个指令执行的顺序。(这里注意IR不是IP,IP内存放的不是机器指令,而是存放指向下一跳要执行指令的地址。IR和IP的关系是:从IP指向的地址中取出指令存放到IR中,然后IP指向下一条指令)
    IA-32的寄存器包括8个通用寄存器(EAX,EBX,ECX,EDX,EDI,ESI,ESP,EBP),1个指令指针寄存器(EIP)、一个CPU状态寄存器(EFLAGS)以及6个段寄存器,这些寄存器的作用主要是临时存储数据和地址(EIP始终指向下一条待执行指令的地址),供cpu操作。

  3. 总线
    总线在物理上不过是若干根导线的集合,用于连接其他的芯片。在逻辑上分为地址总线、数据总线、控制总线。
    地址总线
    CPU要将内存中的指令和数据取出,就必须访问内存中的地址,当CPU访问内存中的某个地址时,地址线上就保持着那个地址值。地址总线能表示多少个不同的数值,这个CPU就能够访问多少内存地址。(如宽度为N的地址线,它能够寻址的空间为0-2^N)
    数据总线
    在CPU和内存中传输指令和数据
    控制总线
    控制总线式不同控制线的集合,是一个总称。有多少条控制总线,就意味着CPU能够对外界提供多少种控制。控制总线的宽度决定了CPU控制外界设备的能力。

计算机中的CPU是如何运转的呢?

还是看图说话:
IA-32体系结构粗浅认识_第2张图片
我们假设操作系统此时已经将程序执行所包含的指令和数据都装载入了内存,CPU开始执行这个程序的一条指令,为了体现CPU和内存的交互,我们假设这条指令使用了内存操作数,之行完指令之后又将结果写回内存:
步骤1,取指令。CU从内存中取指令。即从EIP寄存器指向的地址中取得指令,并将EIP指向下一条待执行指令。
步骤2,解码。CPU(其实是CU中的解码器)完成对该指令的解码。弄明白要进行怎样运算。
步骤3,取操作数。由于之前假设这条指令涉及内存操作数,那么执行这条指令时CPU就要花费一些时钟周期去内存中读数据。读取数据的大致流程如下:CU计算所要获取的数据所在的内存地址,将地址放在地址线上,然后将读取线拉到低电平,提示内存需要读入数据,内存将地址线上的地址对应内存单元的数据放置在数据总线上,读取线置为1,通知CPU将数据读入CPU内的寄存器中。
步骤4,执行。CU将寄存器中的输入操作数传入ALU进行指定计算。ALU将计算结果传入指定位置(寄存器或内存),并更新EFLAGS寄存器中的值。
步骤5,从步骤一开始循环。

以上就是CPU执行指令的大致流程,其中每个步骤至少消耗一个时钟周期,可以看出整个流程中控制单元CU起到了主导作用。

对以上那些CPU基础知识有所了解之后,现在进入正题——IA-32体系结构:

我理解中的IA-32体系结构,是Intel处理器及其相连器件的运转方式、特性与其内存管理模式的结合。之前已经介绍了IA-32的CPU大致结构和运转流程。

如前所述,IA-32是1985年的intel 80386到目前的奔腾系列处理器所沿用的intel处理器架构,在沿用的过程中作了不少改进,比如超标量、流水线、分支预测及超线程等等(以上那些我只懂一些概念,大家百度一下就好,这里着重讲一下IA-32的几种操作模式的内存管理和8086的分段寻址机制)

操作模式:

  • 实地址模式
    IA-32为了兼容8086时代的那些需要直接访问硬件的MS-DOS程序所支持的一种操作模式。这种模式实现了8086处理器的程序设计环境。对于8086处理器,其CPU运行机制应该和前面所说的差不多,具体的构造我没有去研究过,这里主要讲讲8086的内存管理模式:
    8086处理器有20条地址线,也就是说地址线能够寻址1M的内存,但是,8086只有16位的指针寄存器,这就使对内存的寻址产生了问题,即如何用16位的寄存器寻址20位的地址(要知道16位的地址只能寻址64k内存,如果仅以16位来寻址,就浪费了960k的内存空间,内存在当时可是金贵的很啊。。)。为了避免这种暴殄天物式的浪费,8086的设计者们提出了一种“段:偏移”内存管理方式,使16位的指针能够尽量访问到所有可用的内存。
    在讲这个管理方式之前先说一下,我认为,所谓内存管理方式,就是为装载入内存的程序服务的,也就是内存管理解决的是程序载入内存的什么位置、如何访问内存中的指令和数据这类问题,所以接下来的讲解我就以内存中的单个程序为主体来讲。
    一个程序装入8086的内存,首先想到的是这个程序应该被放到哪?我们假设这个程序被放在了一个任意的地址0xABCDE(20位)上。那么现在给你两个16位的指针寄存器,如何访问这个程序所在的每个内存单元呢?自然而然的,你会想到将地址值分开存储,比如地址值的前16位存到寄存器A,后4位存到寄存器B。当时的8086设计者们也是这么想的,但是随之而来的是一个显而易见的问题:后一个寄存器只用了4位,是不是有点浪费?于是设计者们就干脆想办法把后一个16位的寄存器都用上,即后一个寄存器用于存储当前指令相对于程序起始地址的偏移量,于是就产生了“偏移”的概念。那么问题又来了,寄存器A只有16位啊,还是不能找到20位的首地址啊(当时的设计者们肯定不像我说的这么二2333),为了让16位的寄存器A能够存储一个20位的首地址,设计者们规定,每个程序的首地址只能够从0xXXXX0开始(X为一位任意16进制数)这样,默认首地址的最后一位是0,就可以用16位寄存器来确定首地址啦!而以这个起始地址开头往后的64k(偏移的最大值)空间,就称为“段”,段的地址一般存在CS,DS,SS,ES,FS,GS这些段寄存器中。
    于是,8086中所有的程序段其的起始地址都是16的整数倍,每个段的长度最多为64k,如果一个程序的一个段(程序中有许多段,如代码段、数据段、堆栈段等)的大小超过了64k怎么办?这个问题。。我也不知道。。猜测过去可能是因为当时的程序达不到有一个程序段超过64k的规模吧。。
  • 保护模式
    进入32位时代以后,终于不用使用麻烦的“段:偏移”来访问内存了,32位的指针允许计算机方便地访问所有的内存地址,寻址方便了之后,随着内存可使用空间的增大(32位地址空间为4G),设计者们开始设计更好的内存管理方式:
    a.平坦分段模式
    所有载入内存后的程序的段地址都存储在一个“段描述符表”中,每个程序使用表中的一个“段描述符”(其实就是一个64位值),我们来看看段描述符的结构:
    IA-32体系结构粗浅认识_第3张图片
    基址存储的就是这个程序在内存中的第一个可用地址,界限是这个程序占用内存单元的数量,访问类型规定了这个段中的数据是如何被访问的(如只读,只写,读写等)。通过访问段描述符表中的段描述符,就可以找到本程序各段的位置。
    b.多段模式
    多段模式给每个程序都分配了一个段描述符表来存放本程序的每个段。我觉得如果把平坦分段模式比作大锅饭的话,多段模式就是一个个独立的炒菜了。每个程序可以方便地找到自己的段。(可能两种模式各有利弊吧,网上没找到详细的说明)
    c.分页模式
    分页模式是现代计算机比较常用的内存管理策略,分页模式产生的原因主要是由于现在的程序规模较大,而且处理器一般都支持多进程管理,即内存中可以同时运行多个程序。试想如果所有的程序都必须完全装入内存,那么32位机4G的内存肯定是不够的。这就有了“分页”的管理模式。
    每个程序拥有4G的虚拟内存(虚拟内存的具体内容和分页机制大家可以在《程序员的自我修养》这本书中或者goole里看到,我这里着重讲讲自己对分页机制的理解),虚拟内存需要映射到物理内存中才可以被CPU加载执行,为了尽量节省内存空间,操作系统将内存以4KB为单位(一般情况下)将内存分为一片片的空间,一个程序加载时只加载目前需要用到的段到某个页,如果运行过程中出现了当前页中没有所需指令或数据,就引发一个“页错误”,这时操作系统就将需要的代码再加载到当前页,将当前页中暂时不需要的指令覆盖。
    写的有点乱。。大部分东西都没提到大家将就看看,欢迎讨论。

你可能感兴趣的:(IA-32汇编学习)