精华内容
下载资源
问答
  • 实验报告基于 x86 体系结构分析 linux-2.6.26 的启动过程 仅考虑 32 位体系结构 不考虑多核多处理器 要求 1分析流程按照开机 -->BIOS-->grub-->Linux 的顺序进行到 start_kernel 结束 要求 2结合几个关键的 Makefile...
  • 3.2 x86体系结构

    2019-09-20 21:01:44
    3.2 x86体系结构 X86是商业上最为成功,影响力最大的一种体系结构。但从技术的角度看,它又存在着很多的问题,那我们就来一起分析X86这种体系结构的特点。 要探讨x86体系结构,我们就得从8086开始说起。8086是...

    计算机组成

    3 指令系统体系结构

    3.2 x86体系结构

    Screen Shot 2018-05-27 at 18.30.58

    X86是商业上最为成功,影响力最大的一种体系结构。但从技术的角度看,它又存在着很多的问题,那我们就来一起分析X86这种体系结构的特点。

    Screen Shot 2018-05-27 at 18.31.09

    要探讨x86体系结构,我们就得从8086开始说起。8086是英特尔在1978年推出的一款16位的微处理器。

    Screen Shot 2018-05-27 at 18.31.19

    它内部的通用寄存器为16位,对外有16根数据线和20根地址线,因此可以访问的存储单元数量为2的20次方,也就是1MByte。而CPU发到存储器的地址,我们称为物理地址,8086的物理地址采用了段加偏移的方式,关于这一点我们在后面进行解释。8086作为微处理器,在一个芯片上集成了原先由多个部件组成的CPU的功能,因此可以以低廉的价格,很小的体积和功耗,提供相对不错的性能,这也为个人计算机的发展奠定了基础。

    Screen Shot 2018-05-27 at 18.31.30

    1981年IBM就推出了自己的个人计算机 PC 5150。它采用了8086的简化版本8088,这是一款8位宽的微处理器,此后个人计算机的市场迅速增长,反过来也推动了x86微处理器的发展。

    Screen Shot 2018-05-27 at 18.31.41

    1982年英特尔推出了80286,由于应用程序对内存空间的需求越来越大,286将地址总线扩展到了24位,可以寻址16兆的内存空间,而且更多的任务也要求更高效的管理模式。286引入了保护模式,虽然这个机制还不完善,但是是一个非常重要的进步。为了兼容8086,286仍然保留了8086的工作模式,这就被称实模式,而兼容为之前处理器开发的软件成为x86能够成功的关键因素之一。

    Screen Shot 2018-05-27 at 18.31.52

    实模式又称为实地址模式。运行在实模式下的x86处理器就像是一个更快的8086,而且现在所有x86处理器在加电或复位后首先进入实模式。在实模式中会运行系统初始化程序,为后面进入保护模式做好准备。现在的个人计算机在启动之后,CPU首先会通过主板上的芯片组,从BIOS芯片中取出指令进行执行,这段程序就是运行在实模式下的。

    Screen Shot 2018-05-27 at 18.32.02

    那即使286将地址总线扩大到了24位,很快也无法满足应用程序的需求,所以英特尔在1985年将x86体系结构升级到32位,并将之命名为IA32,IA是Intel Architecture的缩写。

    Screen Shot 2018-05-27 at 18.32.11

    386是x86系列中的第一款32位微处理器,它的内部真正支持了32位的算术和逻辑运算,并提供了32位的通用寄存器,地址总线也扩展到了32位,这样就可以寻址到\(2^{32}\),也就是 4GByte 的内存空间。386还在286的基础上改进了保护模式,此外还增加了虚拟8086模式,以运行兼容8086的程序。

    Screen Shot 2018-05-27 at 18.32.20

    在386之后,x86的微处理器的主要工作模式就是保护模式。保护模式为多任务的执行提供了很好的支持和保护,同时可以访问4G字节的物理存储空间,这相比之前是一个很大的进步,可以在相当长的时间内满足软件的需求。而且从386开始还引入了虚存的概念,可以对存储空间进行更好的管理。简单说来保护模式,可以让操作系统加强对应用软件的控制,使得系统运行更加安全高效。

    Screen Shot 2018-05-27 at 18.32.39

    现在在系统启动后,会首先进入实模式,在初始化完成后,通过设置CPU中的控制寄存器,进入保护模式,操作系统和应用程序都运行在保护模式下。如果需要运行兼容8086的程序,可以从保护模式下切换到虚拟8086模式。在这种情况下,如果发生中断或异常,是会回到保护模式处理,处理完成后,再返回虚拟8086模式。不管在哪种模式下,经过复位以后都会重新从实模式开始启动。

    Screen Shot 2018-05-27 at 18.32.48

    32位的x86,也就是IA32体系结构,维持了很长时间,并逐渐占据了市场的主导地位,但是随着应用对内存空间的需求越来越大,32位的体系结构已经难以满足要求。在x86向64位升级的过程中,x86-64的体系结构是最先由AMD提出来的。而英特尔当时正专注于另一种64位的体系结构,称为IA64,但它与IA32是不兼容的。

    Screen Shot 2018-05-27 at 18.32.57

    这就是第一款64位的x86微处理器,AMD的皓龙。它可以访问高于4G字节的存储器,从而解决内存空间受限的问题,其实x86扩展到64位实在说不上是一个亮点。因为在当时已经有很多体系结构都已经升级到了64位,但是由于x86一直坚持着向前兼容的设计原则,聚集了大量的应用程序,形成了很好的生态环境,因此AMD的x86-64的更重要的一点是,它能够兼容32位的x86程序,而且运行这些程序不会降低性能。与此对应的是英特尔提出的IA64体系结构,这是基于这个结构的安腾处理器,它与IA32体系结构并不兼容,只是提供了一种模式,可以运行32位的x86程序,但是会大大降低性能,这就成为了安腾不受欢迎的重要原因之一。中间曲线图是英特尔对安腾销售的预期,这条蓝色的线是97年的预测,第二条线是98年的预测,第三条线是99年的预测,实际的销售情况是这条橙色的线所表示的,所以最后英特尔也回过头来采用AMD的扩展方案。

    Screen Shot 2018-05-27 at 18.33.08

    这就是x86-64,x86-64支持原有的32位的x86的运行模式,并将之命名为传统模式。新增的模式称为长模式。长模式又分为两个子模式,64位模式和兼容模式。在兼容模式下,原有的x86程序不需要重新编译,就可以高效的运行,这也是x86-64得以成功的关键。

    Screen Shot 2018-05-27 at 18.33.17

    x86体系结构从16位演变到64位,其中有很多的变化,我们来看两个重要的方面。第一是寄存器模型的变化。

    Screen Shot 2018-05-27 at 18.33.28

    这是8086所使用的寄存器。我们分别来看它各自的功能。

    Screen Shot 2018-05-27 at 18.33.37

    在通用计算机中,前四个又称为数据寄存器,它们都是16位寄存器,但也可以当做两个八位寄存器使用。大多数算术运算和逻辑运算都会使用到这些数据寄存器,用于临时保存数据,而且它们除了作为通用的用途之外,还有一些专门的用途。

    Screen Shot 2018-05-27 at 18.33.47

    接下来的四个寄存器,又被称为指针寄存器和变址寄存器。SP和BP这两个寄存器用于堆栈操作,SI和DI这两个寄存器用于串操作。这是它们名称的含义,之后在介绍相关的指令时,会讲解它们的具体用途。当然这些寄存器也可以当做普通的数据寄存器使用。

    Screen Shot 2018-05-27 at 18.33.57

    我们再来看标志寄存器,标志寄存器当中包含的若干标志位,标志位分为两大类。

    状态标志用于反映CPU的工作状态。比如说执行完加法运算后,如果产生进位,就会将标志寄存器当中体现进位的状态位置为有效。

    而控制标志则是对CPU的运行起特定的控制作用。例如,如果让CPU采用单步的方式,而不是连续执行指令的方式运行,则需将标志寄存器当中的某个控制标志置为有效。

    Screen Shot 2018-05-27 at 18.34.08

    这是8086的标志位,实际只使用了其中九个比特。红色这些是状态标志,例如比特0就是进位标志。另外三个紫色是控制标志。这些标志位我们也会结合着指令进行讲解。

    Screen Shot 2018-05-27 at 18.34.21

    从8086到IA32,寄存器模型也由16位扩展到了32位。通用寄存器都在原有的基础上增加了高16位,扩展成了32位。例如,原来的AX扩展成32位以后,新的名称为EAX。程序中使用EAX寄存器就是一个32位寄存器,但仍然可以使用AX访问16位,也可以使用AH或者AL访问其中的特定的8位。再来看指令指针寄存器也从16位扩展到32位,标志寄存器也是同样。但是段寄存器的长度没有扩展,而是增加了两个16位的段寄存器。

    Screen Shot 2018-05-27 at 18.34.43

    我们再来看x86-64对寄存器模型的扩展。x86-64将大多数寄存器扩展成了64位,例如EAX扩展了高32位以后成为了RAX,和之前一样,不但可以使用64位的RAX寄存器,同时也可以使用EAX、AX等等。需要注意的是,段寄存器仍然没有扩展。此外,x86-64还增加了八个64位的通用寄存器,命名为R8到R15。

    Screen Shot 2018-05-27 at 18.34.54

    然后我们来看x86体系结构演变过程中的另一个要点,就是存储器的寻址。

    Screen Shot 2018-05-27 at 18.35.04

    8086依靠指令指针寄存器,也就是IP寄存器进行寻址。IP寄存器中保存了一个内存地址,指向当前需要取出的指令。当CPU从内存中取出一个指令后,IP寄存器会自动增加指向下一个指令的地址。程序员不能直接操作IP寄存器,但是可以通过编写转移等指令来改变IP寄存器的内容。我们知道8086是16位的,我们使用IP寄存器可以访问到的内存空间就是2的16次方,也就是64k个字节单元。但其实8086对外有20位的地址线,寻址范围是1兆的字节单元。那如何解决这个矛盾呢?

    Screen Shot 2018-05-27 at 18.35.13

    8086中提供了段寄存器来解决这个问题。8086一共提供了四个段寄存器:CS是代码段寄存器,DS是数据段寄存器,ES是附加段,SS是堆栈段。

    Screen Shot 2018-05-27 at 18.35.24

    现在我们就来看8086的物理地址是如何生成的?我们在编写程序时直接给出的地址称为偏移地址,又叫偏移量,这是16位的。而段寄存器中存放了段基址,这一对地址就被称为逻辑地址。其中段基址在计算时首先被左移四位,然后和偏移量相加,这样就得到了一个20位的物理地址,这也就是从逻辑地址生成物理地址的过程。二进制的左移四位,实际相当于十进制的乘以16,所以物理地址相当于段基址乘以16,再加上偏移量。这就是8086段加偏移的物理地址生成方式。

    Screen Shot 2018-05-27 at 18.35.34

    用这样的方式,8086对存储器进行分段式的管理,每个段最大为2的16次方,也就是64k个字节,他们在编程时使用逻辑地址,这样的好处在于,如果我们将程序在存储器当中变换位置,那程序中原来编写的偏移地址都不需要改动,只需要修改段寄存器的内容就可以了。而且还要说明,不同的逻辑段实际上是可以有重叠的。这也为高效的利用存储器的空间提供了便利。

    Screen Shot 2018-05-27 at 18.35.43

    我们来看一个实例。如果我们写一条指令 MOV AX, [3000H],但实际上在段加偏移的模式下,操作数是默认存放在DS也就是数据段寄存器所指向的段中,让我们假设事先已经在DS段寄存器当中存入了2000H。那么实际的物理地址应该采用段寄存器乘以16,加上偏移量的方式得到最终我们所用的物理地址是23000H。

    我们来看左边这个存储器的片段,假设代码段在图示位置,其中有一条指令就是我们现在所用的MOV指令。第一个字节是操作码,后两个字节就是指令中指定的偏移地址。那CPU在取回这条指令后,发现要从存储器中取得一个数据,因此CPU会取出DS寄存器的内容,并将其左移四位后,与指令编码中的偏移量相加,从而得到了23000这个地址,然后用这个地址去访问存储器。因为我们的目标寄存器AX是16位的,所以从存储器中取出两个字节,并依照着高地址放在高字节,低地址放在低字节的原则,存放到AX当中。这就完成了这条指令的执行。这样段加偏移的方式实际上是非常复杂的,这是在内部的运算器和通用寄存器只有16位的情况下,又想访问超过16位的地址空间所想出的一个折中的办法,算是在简陋的条件下用了一个巧妙的方法,从而提供了较好的性能。但是如果不仅限于微处理器,计算机中是早就采用了32位的地址空间的。

    Screen Shot 2018-05-27 at 22.20.10

    比如60年代的大型计算机 IBM S/360,在其比较早的型号中就使用了32位的通用寄存器,CPU对外发出32位的地址。

    Screen Shot 2018-05-27 at 18.35.54

    我们再来看IA32体系结构的存储器寻址。我们以指令的寻址为例,原先16位的IP寄存器已经扩展为了32位的EIP寄存器。当然在实模式下面还是使用 CS:IP 这样的逻辑地址。而在保护模式下,因为ERP寄存器的寻址能力已经达到了 \(2^{32}\),也就是4G个字节单元,这和CPU对外的地址总线的宽度是一致的,也就是说EIP寄存器完全有能力访问到外部存储器的每一个字节单元,但在保护模式下,IA32仍然采用了与之前形式上一致的寻址模式 CS:EIP 这样的逻辑地址的组合,但是在这种模式下,段寄存器 CS 的含义已经发生了变化。

    Screen Shot 2018-05-27 at 18.36.04

    实际在保护模式下,段基址是放在内存中的。这是一个存储器的片段,这些数据以八个字节为一个单位,我们称为一个描述符。我们来看其中一个描述符,字节2、3、4、7,一共四个字节,保存了一个基地址。而CPU中,CS段寄存器不再保存段基址,而是指向这个描述符的地址,但是我刚才说过CS只有16位,它无法在4G空间中寻址,所以CPU当中还增加了一个寄存器,叫 GDTR。而 GDTR 中则保存了这个全局描述符表的起始地址,CS 段寄存器中则保存了相对于这个起始地址的偏移量,所以我们可以这么理解,CPU在取指令时,先将 GDTR 中保存的地址取出,并于CS寄存器当中的内容相加,得到一个内存地址,用这个地址去访问存储器,获得了这个描述符对应的八个字节,再将其中的基地址部分提取出来,与EIP寄存器中保存的偏移地址相加,就得到了真正要访问的地址。当然在这个描述中还描述了这个段有多长,也就是段界限;以及对这个段的内容是否能读写,也就是权限;还有一些其它的信息。CPU通过这些信息可以由硬件来判断,当前要执行的这条指令是否符合这个段的要求,从而起到了保护的作用。我们可以看到分段的方式变得越来越复杂,虽然它提供了很好的保护机制,但是代价也是不小的。而且在分段的基础上,还可以采用分页等机制,进行更细粒度的管理,所以在有些系统上就对分段进行了简化的处理,让所有的程序都运行在一个段上,也就是不改变段寄存器的内容,而在x86-64上得到了进一步的简化。

    Screen Shot 2018-05-27 at 18.36.15

    图中一个x86-64当中对应的描述符,我们可以看到,很多的字节都被固定设置为零。也就是说,这时候已经没有了段基址和段界限,所有的代码段都是从地址0开始的。在描述符中只设置了这个段对应的权限和若干的控制位。

    Screen Shot 2018-05-27 at 18.36.25

    这些就是x86体系结构演变过程中的一些要点。现在我们已经了解了x86体系结构的基本特点,之后,我们将通过分析x86的具体指令,来进一步学习这种体系结构。

    转载于:https://www.cnblogs.com/houhaibushihai/p/9097982.html

    展开全文
  • 文章目录In this article - 此文Register - 寄存器Calling Conventions - 调用约定Debugger Display of Registers and Flags - 调试器显示寄存器与标记x86 Flag - x86 的标记位Conditions - 条件Data Types - 数据...


    系列目录: Processor Architecture - 处理器体系架构
    原文: x86 Architecture

    In this article - 此文

    Intel x86 处理使用的是复杂指令集的计算机(complex instruction set computer (CISC),还有什么是RISC?RISC架构)体系架构,意味着使用的是少量的特定寄存器的数量,而不是使用巨量的通用寄存器的方式。也意味着复杂的特定指令将更有优势。

    x86 处理器至少可以追溯到 8位的Intel 8080 处理器。x86指令集中的许多特性都是原由于处理器(及其Zilog Z-80变体)的向后兼容性。

    Microsoft Win32 的Flat Mode(平坦模式1)使用的是 x86 处理。这个文档将近关注于在flat mode上的。

    Register - 寄存器

    x86 体系架构由以下非特权的整形寄存器组成。

    寄存器描述
    eax累加器,运算用(Accumulator)
    ebx基地址寄存器(Base Register)
    edx数据寄存器 - 用于I/O端口访问及算术函数(Data Register)
    esi源索引寄存器(Source index register)
    edi目标索引寄存器(Destination index register)
    ebp基地址指针寄存器(Base poinster register)
    esp栈指针寄存器(Stack pointer)

    所有整形寄存器都是32位的。然而,有许多的16位或是8位的子寄存器。

    寄存器描述
    axeax的低16位寄存器(Low 16 bits of eax)
    bxebx的低16位寄存器(Low 16 bits of ebx)
    cxecx的低16位寄存器(Low 16 bits of ecx)
    dxedx的低16位寄存器(Low 16 bits of edx)
    siesi的低16位寄存器(Low 16 bits of esi)
    diedi的低16位寄存器(Low 16 bits of edi)
    bpebp的低16位寄存器(Low 16 bits of ebp)
    spesp的低16位寄存器(Low 16 bits of esp)
    aleax的低8位寄存器(Low 8 bits of eax)
    ahax的高8位寄存器(High 8 bits of ax)
    blebx的低8位寄存器(Low 8 bits of ebx)
    bhbx的高8位寄存器(High 8 bits of bx)
    clecx的低8位寄存器(Low 8 bits of ecx)
    chcx的高8位寄存器(High 8 bits of cx)
    dledx的低8位寄存器(Low 8 bits of edx)
    dhdx的高8位寄存器(High 8 bits of dx)

    操作子寄存器影响的仅仅只有子寄存器,而不会影响之外的其他子寄存器。例如,储存一些数据到 ax 寄存器,eax 剩余的高16位寄存器是不会有变化的。

    当使用 ?(计算表达式) 命令,寄存器的前缀要带上 “at” 标记(@)。例如,你使用使用 ? @ax 而不是 ? ax 。确保调试器能认出 ax 是个寄存器(Register)而不是一个 标记符号(Symobl)。

    然而,在r(寄存器)命令下不需要(@)。例如, r ax = 5 将会被正确解析。

    还有其他两个表示处理器当前状态的重要寄存器:

    寄存器描述
    eip指令指针寄存器(instruction pointer)
    flags标志寄存器(flags)

    指令指针寄存器(instruction pointer)指向的是即将被执行的指令的地址。

    标记寄存器(flags register)是单个bit位(single-bit)的标记集合。许多的指令都会修改 flags 的为,代表指令的结果。这些flags标记可以被后续的条件跳转指令(conditional jump)用于测试用。详情查考 x86 Flags

    Calling Conventions - 调用约定

    x86 体系架构有一些不同的调用约定。还好的是,他们都是下面一些寄存器的备份恢复与方法返回规则:

    • 函数必须备份恢复所有寄存器,除了 eaxecxedx,通过call可以改变这些寄存器,而 esp,必须通过对应的调用约定来更新。(译者jave.lin:文档真的不用心,call汇编是先push eip + sizeof(eip),就是先把call的以下个指令的地址push到栈中,在跳转到方法签名中的函数指令地址,所以有两个步骤在里头,但这个文档说的不够详细,这微软写x86指令文档这么儿戏的吗?手动哭笑一下。
    • eax 寄存器用于函数结果的<=32位的返回值。如果结果是64为的,那么如果储存在 edx:eax 寄存器对中。

    下面是 x86 体系架构的调用约定项:

    • Win32( __stdcall
      函数的参数是通过从右向左的传入栈中,并且是在被调用函数(callee)中清理栈的。
    • C++内置的方法调用(大家了解的:thiscall
      函数参数也是从右向左传入栈,this指针是通过 ecx 寄存器传入的,也是在callee中清理栈的。
    • COM(__stdcall C++的方法调用)(译者jave.lin:这不就和上面的Win32一样了么?
      从右向左,this指针会传入,callee清理栈(描述意思和上面thiscall一样,这里简写,不知道为何要多此一举
    • __fastcall
      头两个 DWORD或是更小的字(DWORD-or-smaller) 参数通过 ecxedx 寄存器传入。剩下的参数都会传入栈,也是从右向左,也是callee清理栈。
    • __cdecl译者jave.lin:cdecl就是:c declaration,就是C中声明用的,为何微软不写这些,这些多么容易理解的点
      函数参数也是从右向左传入栈,但清理栈在调用函数(caller)处理。__cdecl调用约定一般用于变长参数数量。(译者jave.lin:如printf(const char *format, …),因为参数在调用函数是知道的,所以可以清理变长的参数栈

    (函数 从左向右入栈 的有:FortranPascal语言,但实际我没去验证过,在此篇博客上看到:系统栈的工作原理

    下面我来列个表格,好理解一写

    Calling Convention Type(调用约定类型)Passing Parameters (参数传递)Cleaning Stack(栈清理)
    __stdcall从右到左被调用函数
    __fastcall从右到左被调用函数
    __cdecl从左到右调用函数

    C++内置的对象方法调用还需传递this对象指针

    Debugger Display of Registers and Flags - 调试器显示寄存器与标记

    这里有个调式器显示寄存器的示例:

    eax=00000000 ebx=008b6f00 ecx=01010101 edx=ffffffff esi=00000000 edi=00465000
    eip=77f9d022 esp=05cffc48 ebp=05cffc54 iopl=0         nv up ei ng nz na po nc
    cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000286
    

    在用户模式(user-mode)下调式,你可以忽略掉 iopl 与最后一整行(cs=001b ss=0023 ds=0023 es=0023 fs=0038 gs=0000 efl=00000286)的调式信息。

    x86 Flag - x86 的标记位

    在之前的例子中,第二行结尾两个字母的代码都是flags(标记为)(nv up ei ng nz na po nc)。这些都是 单bit位的寄存器,并且各方面都用到。

    下面是 x86 中的 flags 的列表:

    下面的列表格式微软中的文档乱七八糟的,排版出了问题,没人发现,没人处理?这里我自己排版一下,x86 Flags 的文档也可以看这个

    Flag CodeFlag NameValue Flag StatusStatus Description
    of溢出标记(Overflow Flag)0 nv,1 ov无溢出(No overflow), 溢出(Overflow)
    df方向标记(Direction Flag)0 up, 1 dn上方(Direction up), 下方(Direction down)
    if中断标记(Interrupt Flag)0 di, 1 ei中断禁用(Interrupts disabled), I中断启用(nterrupts enabled)
    sf符号(签名)标记(Sign Flag)0 pl, 1 ng正数或零(Positive (or zero)), 负数(Negative)
    zf零标记(Zero Flag)0 nz, 1 zr非零(Nonzero), 零(Zero)
    af辅助进位标记(Auxiliary Carry Flag)0 na, 1 ac无辅助进位(No auxiliary carry), 有辅助进位(Auxiliary carry)
    pf校验标记(Parity Flag)0 pe, 1 po偶校验(Parity even), 奇校验(Parity odd)
    cf进位标记(Carry Flag)0 nc, 1 cy无进位(No carry), 有进位(Carry)
    tf捕获标记(Trap Flag)-如果 tf 等于1,在执行一个指令后处理器将会抛出 STATUS_SINGLE_STEP 异常。这个标记用于调式器实现 单步调试(single-step)的跟踪。这不应用于其他的应用程序。

    iopl
    I/O特权等级(I/O Privilege Level)

    这是一个2 bit 位的整型,值为:0~3之间。用于操作系统控制访问硬件用的。不应该用于应用程序。

    当寄存器被显示在调试命令窗口中作为结果,它是以flag status方式显示。然而,如果你想调整flag,可用 r (Registers) 命令,你应该它当做是flag code。

    在WinDbg(Windows 调试)的寄存器窗口中,flag code可展示或是修改flags。flag status方式是不支持的。

    这里有个例子。之前的寄存器显示中,ng 的flag status有显示。这意味着符号标记的bit为当前为1。要修改的话,使用下面的命令:

    r sf = 0
    

    这是将符号标记设置0。如果你要显示其他的寄存器,ng 标记状态码(status code)将不会显示。而是会显示 pl 状态码。

    符号标记(Sign Flag),零标记(Zero Flag),和进位标记(Carry Flag)都是最常用的标记。

    Conditions - 条件

    一个条件描述的是一个或多个标记的状态。在x86 所有操作条件都可有条件术语表示。

    汇编器使用一个或多个字母来表示条件。一个条件可以用乘号缩写来表示。例如:AE(“above or equal(高于或等于)”)与NB(“not below(不低于)”)是一样的条件。下面列表中列出一些常见的条件和他们的意思:

    Condition Name(条件名称)Flags(标记)Meaning(意思)
    ZZF=1最后的操作结果为0。
    NZZF=0最后的操作结果非0。
    CCF=1最后的操作需要进位或借位。(对于无符号整型而言,这代表溢出。)
    NCCF=0最后的操作不需要进位或借位。(对于无符号整型而言,这代表溢出。)
    SSF=1最后的操作结果有高位bit集。
    NSSF=0最后的操作结果没有高位bit集。
    OOF=1当处理有符号整型操作是,最后的操作有上溢出或是下溢出。
    NOOF=0当处理有符号整型操作是,最后的操作无上溢出或是下溢出。

    条件也可以用于比较两个值。cmp 指令比较两个操作数,然后设置标记位,就像一个数减去另一个数。下面的条件可用于检查 cmp value1, value2的结果。

    Condition Name(条件名称)Flags(标记)Meaning after a CMP operation(CMP操作后的意思)。
    EZF=1value1==value2
    NEZF=0value1!=value
    GE NLSF=OFvalue>=value2。这些值被当作有符号整型。
    LE NGZF=1 or SF!=OFvalue1<=value2。这些值被当作有符号整型。
    G NLEZF=0 and SF=OFvalue1>value2。这些值被当作有符号整型。
    L NGESF!=OFvalue1<value2。这些值被当作有符号整型。
    AE NBCF=0value1>=value2。这些值被当作无符号整型。
    BE NACF=1 or ZF=1value1<=value2。这些值被当作无符号整型。
    A NBECF=0 and ZF=0value1>value2。这些值被当作无符号整型。
    B NAECF=1value1<value2。这些值被当作无符号整型。

    条件通常用于对 cmp 或是 test 指令结果的操作。例如:

    cmp eax, 5
    jz equal
    

    用表达式 (eax - 5) 来计算比较 eax 寄存器 与 5 数字,并根据结果设置对应的标记位。如果差为0,那么 zr 标记位将被设置,并且 jz 条件会为帧,因此将会跳转处理。

    Data Types - 数据类型

    • byte : 8 bits
    • word : 16 bits
    • dword : 32 bits
    • qword : 64 bits(包含双精度浮点)
    • tword : 80 bits(包含扩展的双精度浮点)
    • owrod : 128 bits

    Notation - 符号

    下面表格的符号代表用于汇编语言指令的描述:

    NotationMeaning
    r, r1, r2…寄存器
    m内存地址(查看后续的寻址模式小节了解更多信息)。
    #n立即数
    r/m寄存器 或 内存
    r/#n寄存器 或 立即数
    r/m/#n寄存器 或 内存,或 立即数
    cc之间的条件小节列表中的条件代码。
    T“B”, “W"或"D”(byte, word 或 dword)
    accTSize T accumulator(累加器T的大小):如果是al T = “B”,如果是 ax T=“W”,或是如果 eax T = “D”

    Addressing Modes - 寻址模式

    有一些不同的寻址模式,但他们所有采用 T ptr [expr] 的形式,T 是数据类型(查看之前的 Data Type 数据类型小节), expr 表达式涉及 常数与寄存器。

    多系数的模式的符号可以不太难的就推导出来。例如,BYTE PTR [esi + edx * 8 + 3] 意思是“取 esi 寄存器的值,加上 edx 值的8倍的值,再加上3,然后以结果为地址取去一个字节。”

    Pipelining - 流水线

    什么是Pipelining,可以参考:2

    崩腾有双工问题,这意味着它可以在一个时钟周期可以执行两个行为。然而,什么时候能同时执行两个行为(成对的行为)是非常复杂的。

    因为 x86 是CISC处理器,你不需要担心 跳过延迟槽(jump delay slots) 的问题

    Synchronized Memory Access - 同步的内存访问

    加载、修改,与储存指令都会遇到一个 lock 修改指令的前置问题,如下:

    1. 在发出指令前,CPU将会flush掉所有pending的内存操作,以确保内存数据一致性。预先取到的所有数据将放弃。
    2. 当发出指令时,CPU将独占的访问总线。确保load(加载)/modify(修改)/store(储存)操作的原子性。

    xchg 指令将自动遵循前面的规则,无论何时都会使用内存数据来调整数值。

    所有其他的指令默认无锁。

    Jump Prediction - 跳转预测

    无条件跳转(jumps)都是可被预测。

    有条件跳转的预测可能会也可能不会,依赖于他们最后一次的执行。记录跳转历史的缓存是有大小限制的。

    如果CPU没有记录最后一次的条件跳转是否被执行,那么反向条件跳转将被预测执行,而正向条件跳转不会被执行。

    是文档太绕还是我理解不了,-_-!!!

    Alignment - 对齐

    x86 处理器将自动修正没对齐的内存访问,但会有性能惩罚。也没有异常抛出。

    如果对象的地址大小是整数倍的,那么这一内存访问是被认为是对齐的。例如,所有的BYTE访问都被认为是对齐的(都是1(字节)的倍数),WORD方位偶数地址是对齐的,而DWORD的地址为了对齐必须是4(个字节)的倍数。

    lock 不应该用于无对齐的内存访问。


    1. FLAT 模式 - 因为和 16 位 Windows 下的把代码分成 DATA,CODE 等段的内存模式不同,WIN32 只有一种内存模式,即 FLAT 模式, 意思是"平坦"的内存模式,再没有 64K 的段大小限制,所有的 WIN32 的应用程序运行在一个连续、平坦、巨大的 4GB 的空间中。这同时也意 味着您无须和段寄存器打交道,您可以用任意的段寄存器寻址任意的地址空间,这对于程序员来说是非常方便的。在Win32下编程,有许多重要的规则需要遵 守。有一条很重要的是:Windows 在内部频繁使用 ESI,EDI,EBP,EBX 寄存器,而且并不去检测这些寄存器的值是否被更改,这样当您要 使用这些寄存器时必须先保存它们的值,待用完后再恢复它们。 ↩︎

    2. How Pipelining Works计算机体系结构——流水线技术(Pipelining)计算机体系结构——流水线技术 ↩︎

    展开全文
  • OpenSOC86是Verilog中x86架构的开放实现。 当前版本仅实现16位部分(实模式)。 该处理器是流水线架构,主频为Cyclone II -6级,时钟频率为100 MHz。 因此,可以将其视为类似于实模式中的486。 几个外围设备也以某种...
  • 绝望,x86操作系统 基于... 绝望基于OSDev Wiki的Bare Bones。 请不要讨论这些问题,并写一篇7,000字的文章,说明为什么使用裸骨头不好。 编译中 Desparity不会随交叉编译器一起提供,因此您需要先进行编译,然后再...
  • X86体系的指令

    2019-04-27 13:11:38
    分类:传送类指令(比如move指令),运算类指令(add,inc,dec,adc),转移类指令(jnz),控制类指令(clc)。

    在这里插入图片描述

    分类:传送类指令(比如move指令),运算类指令(add,inc,dec,adc),转移类指令(jnz),控制类指令(clc)。

    展开全文
  • 加载不使用execve syscall的静态链接的ELF二进制文件(x86体系结构)。 runtime-unpack加载静态链接的ELF二进制文件(x86体系结构),而无需执行execve syscall。 使用make编译Build&pack:运行编译后,您可以使用....
  • The_Holy_Book_of_X86:x86体系结构,汇编,内存管理,分页,分段,SMM,BIOS的简单指南。
  • 基于x86体系结构处理器的VME主控模块设计.pdf
  • 氦气 x86体系结构的操作系统
  • Pincpt是用于检查点x86应用程序的体系结构状态的工具。 检查点是人类可读的,旨在初始化用户模式CPU性能模拟器。 当前,pincpt仅适用于32位单线程应用程序。
  • KSOS:我们自己的操作系统是从头开始为x86体系结构构建的!
  • x86_x64体系探索及编程》.(邓志).[PDF]
  • 它在x32和x86体系结构的Windows(Windows 7或更高版本),Linux和MacOS(10.9或更高版本)上可用。 特征 网站 添加,编辑或删除网站 网站数量不限 显示和排序当前网站列表以及响应时间,URL,协议等。 显示列表中...
  • operating-system-x86-64bit:具有TARFS文件系统的x86体系结构(64位)的基本操作系统。 使用INT $ 80支持fork(),cow(),分页,虚拟memoryring3用户进程和syscall。 能够运行shell和二进制文件:ls,cat,echo...
  • x86体系结构的OS开发

    2009-04-05 20:34:46
    一本入门级别的,介绍x86体系结构的OS开发教材。绝对好书,透彻,清晰。
  • This manual is for advanced programmers and software developers who want to make their software faster. It is assumed that the reader has a good knowledge of the C++ programming language and a basic ...
  • x86 体系结构

    千次阅读 2015-05-29 00:36:29
    x86的汇编 Nasm Move a b 将b寄存器的值拷贝到a寄存器   80386有16个寄存器 1 通用寄存器 2 段寄存器 3 状态和质指令寄存器   在任何时候cpu能够访问6个段 段寄存器分别是CS DS ESFS GS SS   CS code segment ...

    x86的汇编

    Nasm

    Move a  b   将b寄存器的值拷贝到a寄存器

     

    80386有16个寄存器

    1 通用寄存器

    2 段寄存器

    3 状态和质指令寄存器

     

    在任何时候cpu能够访问6个段

    段寄存器分别是CS DS ESFS GS SS

     

    CS code segment 代码段,当发生call指令时就自动进行转换

     

    SS stack segment堆栈段,能够显示调用,用户可以动态创建堆栈

     

    EBP ESP ESS在使用中的具体关系?

     

    段寄存器具有和对应的指令有默认的关系,但是可以通过明确指定改变这种关系,除了ES CS SS

     

    异常来自自身的程序的运行

    中断来自cpu外部的中断信号                                           

     

    内存管理,内存逻辑地址翻译为物理地址,经过以下几步:

    1 段地址翻译,将逻辑地址转化为线性地址

    2 分页翻译,将线性地址翻译为物理地址

     

    在32位页地址上,page dir 有1k条记录,占有4kB,也就是一页。每一个pagedir的记录会对应1k个pagetable,1k条page,对应会占有4kB,也就是一页。因此总共会占有1025页,也就是占有内存4k*1025B

     

    Call gate interupt gate trap gate 分别代表 systemcall interupt exception

     

    因为程序对于分页是透明的,也就是程序看到的内存总是存在的,实际上可能是只是一个页表目录和页表,因此存在虚拟内存大于实际内存的情况

    展开全文
  • x86体系结构

    2014-03-28 17:01:53
    abi: 分i386和x86-64 i386的: www.sco.com/developers/devspecs/abi386-4.pdf x86-64的: http://www.x86-64.org/documentation/abi-0.99.pdf system programming: TNND,这个好多啊。。 1, ...
  • x86体系结构的虚拟机研究.kdh 用cajviewer打开
  • x86/x64体系探索及编程》是对Intel手册所述处理器架构的探索和论证。全书共五大部分,从多个方面对处理器架构相关的知识进行了梳理介绍。书中每个章节都有相应的测试实验,所运行的实验例子都可以在真实的机器上...
  • x86体系SSE介绍

    千次阅读 2016-11-28 10:20:44
    最近在看一些并行编程的trick,做一下笔记 ——《并行编程方法与优化实践》  Intel ICC和开源的GCC编译器支持的SSE/AVX指令的C接口(intrinsic,内置函数)声明在intrinsic.h头文件中。其数据类型命名主要有_...
  • Intel x86体系结构的资料

    千次阅读 2009-08-24 17:43:00
    赞莱温斯基的分享精神。先在这里做个记号。以后估计用得着的。 参考:... abi: 分i386和x86-64i386的:
  • 8086的物理地址生成从逻辑地址到物理地址:
  • x86_x64体系探索及编程

    2018-03-03 23:39:19
    本书是对Intel手册所述处理器架构的探索和论证。全书共五大部分,从多个方面对处理器架构相关的知识进行了梳理介绍。...本书适合有一定的x86基础知识,且对了解处理器架构及编程感兴趣的读者阅读。
  • 基于x86体系结构分析linux的启动过程

    千次阅读 2013-05-01 22:17:24
    仅考虑32位体系结构 不考虑多核多处理器 要求1:分析流程按照开机-->BIOS-->grub-->Linux的顺序进行,到start_kernel结束   第一步——>加载BIOS:   打开计算机电源后,计算机会首先加载BIOS信息,这是因为...
  • 本文主要调研了基于x86体系结构的kmemcheck实现策略,给出了模块中主要代码的逻辑调用关系,以及用于功能验证的内核测试模块(详见附录);扼要的评估了kmemcheck工作性能,和基于非x86体系结构的kmemcheck实现可能...
  • 二、x86体系CPU下的寄存器 https://blog.csdn.net/hhw999/article/details/52861036 https://blog.csdn.net/u012493828/article/details/53439226 1、一个典型的CPU由运算器、控制器、寄存器等器件组成,...
  • 每个程序被运行起来以后,它将...硬件决定了地址空间的最大理论上限,即硬件的寻址空间的大小,例如,32位x86体系linux虚拟地址空间是4GB的虚拟空间大小,地址范围是0~2^32-1,即0x00000000~0xFFFFFFFF。 0X00000
  • X86体系中保护模式下的内存访问机制 是一个收费的论文。有兴趣的看看

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 53,173
精华内容 21,269
关键字:

x86体系