ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构
★PART1:32位的x86处理器执行方式和架构
1. 寄存器的拓展(IA-32)
从80386开始,处理器内的寄存器从16位拓展到32位,命名其实就是在前面加上e(Extend)就好了,8个通用寄存器被命名为EAX,EBX,ECX,EDX,ESI,EDI,ESP和EBP,同样的,操作的时候必须要和寄存器的长匹配,比如下面的操作就是错的。
32位通用寄存器的高16位不可以单独使用,但是他们的低16位依然可以按照8086的使用方法一样使用。处理器在32位保护模式下可以使用全部的32条地址线,访问4GB内存。同时,EIP变成了32位的,当然在实模式下也可以只使用实模式下的16位。标志寄存器FLAGS也拓展到16位,其前各个16位和16位模式下各个标志一样。
在32位模式下对内存的访问理论上是不需要分段的,但是IA-32也是基于分段模型的,当然也可以只分一个段,基地址是0x00000000,段的长度是4GB,在这种情况下,可以视为不分段,也就是平坦模式(Flat Mode)。
在32位模式下,处理器要求在加载程序的时候,先定义该程序拥有的段,然后允许使用这些段,定义段的时候,除了基地址(起始地址)外,还附加了段界限,特权级别,类型等属性。当程序访问一个段时候处理器将用固件实施各种检查工作,以防对内存的违规访问,如图,32位模式下,传统的段位寄存器,如CS,SS,DS,ES,保存的不再是16位的段基地址,而是段的选择子(选择要访问的段,段的定义在GDT或者LDT上),除了段选择子,每个段寄存器还包括了一个不可见的部分,称位描述符高速缓存器,里面有段的基地址和各种访问属性,这部分内容程序不可访问,由处理器自动使用,另外32位模式还多加了两个段位寄存器FS和GS。
2. x86的32位处理器的基本工作模式
①:16位保护模式(80286)
80286是一款16位的处理器,在实模式下和8086一样,只能使用最大为64KB的段,虽然他有24根地址线,理论上可以访问224的内存(16MB),但是还是只能分成多个段来访问。但是80286访问内存的时候,不需将段地址左移,因为在段寄存器的描述符高速缓存中有24位的段物理基地址,这样一来,段可以位于16MB内存空间中的任何位置,而不再限于1MB的范围内,也不必非得对齐16字节,但是80286的通用寄存器是16位的,只能提供16位的偏移地址,所以和8086一样,即使运行在16位保护模式下,段的长度也不能超过64KB。
②:32位保护模式(80836和以后的x86 32位处理器)
80386处理器的寄存器是32位的,而且拥有32根地址线,可以访问232,即4GB的内存,80386以及后续的所有32位处理器,都有兼容模式,可以运行实模式下的8086,且刚加电的时候,这些处理器都自动处于实模式下,可以运行8086的程序,这个时候它相当于一个非常快的8086处理器,在实模式下经过一定的设定就可以进入保护模式。32位保护模式下,段的基地址是32位的,偏移地址也是32位的。32位保护模式兼容80286的16位保护模式。
3. 线性地址(Liner Address)
IA-32处理器编程,需要在程序中给出段地址和偏移地址,因为分段是IA-32的基本特征之一,传统上,段地址和偏移地址称为逻辑地址,偏移地址被叫做有效地址(Effective Address,EA),在指令中给出有效的地址叫做寻址方式(Addressing Mode),比如:
段的管理是由处理器的段部件负责产生的,段部件将段地址和偏移地址相加,得到访问内存的地址。一般来说,段部件产生的地址就是物理地址。IA-32处理器支持多任务,在多任务环境下,任务的创建需要分配内存,当任务终止后,还要回收他所占用的内存。在分段模型下,内存的分配是不定长的,所以IA-32处理器支分页功能,分页功能将物理内存空间划分成逻辑空间上的页,页的大小是固定的,一般为4KB,通过使用页,可以简化内存管理。(只有开启了页功能才能使用页),当页功能开启后,段部件产生的地址就不再是物理地址了,而是线性地址,线性地址还要经页部件转换后,才是物理地址。
线性地址的概念用来描述任务的地址空间,IA-32处理器上的每个任务都拥有4GB的虚拟内存空间,这是一段长4GB的平坦空间,就像一段平直的线段,因此叫线性地址空间,相应的,由段部件产生的地址,就对应着线性地址空间上的每一个点,这就是线性地址(也就是线性地址是段部件的一个对应,是物理地址的一个映射)。
4. 流水线(Pipe-Line)
在8086时期,处理器已经有了指令预取队列,当指令执行时,如果总线总是空闲的(没有访问内存的操作),就可以在指令执行的同时预取指令并提前译码,这种做法可以大大地提升程序的执行速度。(原理就是利用处理器的不同指令大多不会再一个时钟周期内完成)。可以把一个指令分解成若干个细小的步骤,并分配给相应的段元来完成。每个单元的执行时独立的,并行的。这么做以后,每个步骤的执行就会在时间上重叠起来,这种执行指令的方法就是流水线技术。根据贪婪算法,流水线的执行效率受执行时间最长的那一级的限制,要缩短各级的执行时间。如果要缩短各级的执行时间,那么就要让每一级的任务减少,与此同时,就需要把一些复杂的任务再进行分解,如此,在2000年的时候Intel推出的Pentium处理器(采用NetBurst微结构,进一步分解指令的执行过程,采用31级超深流水线)。
5. 高速缓存(Cache)
因为与处理器的寄存器相比,内存的访问速度(几十ns)和硬盘的访问速度(几十ms)都非常低慢,所以人们就弄了一个高速缓存的机制,高速缓存说白了就是一个存储器(也是一个静态存储器,容量较小,但速度可以与处理器匹配。),当然这只是简单来说的,事实上高速缓存的实现是很复杂的,里面有很多计算的原理,不同CPU的实现还可能不一样,高速缓存利用了程序运行的时候,总是局部性的(比如访问刚刚访问过的指令或者数据,这被称为程序运行的局部性规律。)每当处理器要访问内存,首先检索高速缓存。如果要访问的内容已经在高速缓存中了,那么直接从高速缓存中获得,这被称为命中(Hit);否则,称为不中(Miss),在不中的情况下,处理器灾区的需要的内容前必须重新装载高速缓存,而不只是直接到内存中去哪个内容,高速缓存的装载是以块为单位的,包括那个所需数据的邻近的内容,为此需要额外的时间来等待块从内存载入高速缓存,在这个过程中损失的时间称为不中惩罚(miss penalty)。
6. 乱序执行(Out-Of-Order Executive)
为了执行流水线技术,需要降至零拆分成更小的可独立执行部分,即拆分成微操作(micro operations),简写为μops。
有些简单指令只用一个微操作,比如:
有些指令可以拆分成两个微操作,一个从内存中读取数据并保存到临时寄存器,另一个用于将EAX寄存器和临时寄存器的数值相加。
再比如:
这个可以拆分成三个微操作一个从内存中读数据,一个执行相加的动作,第三个用于将相加的结果写回到内存中。一旦将指令拆分成为操作,处理器就可以在必要的时候乱序执行,比如:
这里,指令mov [mem2],eax可以拆分成两个微操作,如此,在执行逻辑左移指令的同时,处理器可以提前从内存中读取mem2的内容,典型的,如果数据不在高速缓存中,那么处理器在获取mem1的内容之后,会立即开始获取mem2的内容,于是同时,shl指令已经开始执行了。同理,乱序执行可以大大加快如push,call等指令的执行速度。
7. 寄存器重命名
书上有一个例子:
代码上做了两件事情,一个是将mem1的内容进行左移3个单位,另一个是将mem3的内容+2,如果将后面三个操作所用的寄存器名名称不同的名字,那么这个操作也不会被影响,所以处理器为最后三条指令使用了另一个不同的临时寄存器,因此左移和加法可以并行地进行。再比如:
假定现在mem1的内容在高速缓存中,可以立即取得,但mem2的内容不在高速缓存中,也就是说,算术左移可以在add之前就进行了,所以我们为左移设定一个新的临时寄存器,那么这样eax的内容认识以前的,他将一直保存着这个值,直到ebx的内容就绪,然后和它一同做加法运算。如果没有寄存器重命名机制,左移操作将不得不等待从内从中读取mem2的内容到EBX寄存器中以及加法操作。
在所有操作都完成后,那个代表EAX寄存器的最终结果的临时寄存器的内容被写入真实的EAX寄存中,该处理过程被称为引退(Retirement)。所有通用寄存器,栈指针,标志,浮点寄存器,甚至段寄存器都有可能被重命名。
8. 分支目标预测
如果遇到转移指令,那么后面那些进入流水线的指令将会无效,所以我们要清空流水线,那么为了解决这个问题,Intel引入了分支预测技术,因为程序中可能出现大量的循环,所以遇到转移指令(条件转移指令,loop等),处理器将会预测他下一次还会转移到某个标号处而不是往下执行,在处理器内部,有一个小容量的高速缓存器,叫做分支目标缓存器(Branch Target Buffer,BTB)。当处理器执行了一条分支语句后,它会在BTB中记录当前指令的地址,分支目标的地址,以及本次分支预测的结果,下一次,在转移指令实际执行前,处理器会查看BTB,看看有没有最近转移的记录,如果能找到对应的条目,则推测和上一次相同的分支,把改分支的指令送入流水线。如果预测失败,再清空流水线,刷新BTB。
★PART2:32位的x86系统的指令系统
1. 32位处理器的寻址方式
如果处理器在16位模式下,没有指令前缀0x66,则认为指令是传统的16位寻址方式,如果有指令前缀0x66,则是32位寻址方式,在32位模式下,没有指令前缀0x66,则认为指令是传统的32位寻址方式,如果有指令前缀0x66,则是16位寻址方式。指令默认使用32位宽度的寄存器和32位的立即数,如果存在内存寻址,则偏移量也是32位的。
32位模式下,内存寻址可以使用全部的32位通用寄存器作为基址寄存器,还可以加上一个除了ESP的32位通用寄存器作为变址寄存器,变址寄存器还可以允许乘以1,2,4和8作为比例因子。最后还可以在加上一个8位或者32位的偏移量。比如
2. 操纵数大小的指令前缀
每一条指令都可以拥有前缀,比如重复前缀(rep/repe/repne)、超越前缀指令(如ES:),总线封锁前缀(LOCK)等。前缀是可选得,每个前缀的长度是1个字节,每个指令都可以拥有0-4个前缀。
16位模式下的编码格式和32位的编码格式是一样的,但是解释是不同的,所以编写的时候就要考虑指令的运行环境,为了指明程序的默认运行环境,编译器提供了伪指令bits,用于指明其后的指令应该被编译成16位的,还是32位的(16位模式是默认的编译模式,如果没头指定指令的编译模式,则默认是16bits的)。
3. 一般指令的拓展
32位处理器拥有32位寄存器和算术逻辑部件。而且系统内存芯片之间的数据通路至少是32位的,所有寄存器或者内存单元为操作数的指令都被扩充,这些拓展操作即使是在16位模式下(实模式或者保护模式)都是可以用的,比如最简单的加法指令。
压栈出栈的操作比较特殊。
压入一个字节立即数,则无论是在16位模式下或者是32位模式下,一定要使用byte,关键字byte在push和pop操作上是不会给机器码加上指令前缀的,但是执行的时候,无论什么时候,push(pop)都不会压入(弹出)一个字节的数据,在16为模式下,这两个指令使用SP指针,按照有符号数的规则填充到字节的高八位,然后压栈(弹出),32位使用ESP,按照有符号数的规则填充到高24位再压栈(弹出)。
如果要压入一个字立即数,则无论是在16位模式下或者是32位模式下,一定要使用前缀word,在16位模式下,执行push操作,SP先-2,然后直接压入该字;在32位模式下,会填充字的高16位(按照有符号数的规则),ESP相应-4,然后压入双字,pop操作类似。
如果要压入一个双字立即数,则无论是在16位模式下或者是32位模式下,一定要使用dword,而且栈指针寄存器(SP或者ESP)都要-4。pop类似。
(压入(弹出)通用寄存器可以不用关键字byte,word,dword修饰,内存单元一定要关键字byte,word,dword修饰,处理器压入内存操作行为和压入立即数的行为是一样的)。
压入段寄存器的时候,在16位模式下,先将SP的内容-2,然后直接压入;如果是在32位模式下,则会先将段寄存器的内容用0拓展到32位(高16位全是0),然后,将ESP的内容减去4,然后再压入拓展后的32位的值。(段寄存器能用的只有16位)。
ASM:《X86汇编语言-从实模式到保护模式》第10章:32位x86处理器的编程架构的更多相关文章
- ASM:《X86汇编语言-从实模式到保护模式》第11章:进入保护模式
★PART1:进入保护模式 1. 全局描述符表(Global Descriptor Table,GDT) 32位保护模式下,如果要使用一个段,必须先登记,登记的信息包括段的起始地址,段的 ...
- 存储器的保护(一)——《x86汇编语言:从实模式到保护模式》读书笔记18
本文是原书第12章的学习笔记. 说句题外话,这篇博文是补写的,因为让我误删了,可恶的是CSDN的回收站里找不到! 好吧,那就再写一遍,我有坚强的意志.司马迁曰:“文王拘而演<周易>:仲尼厄 ...
- 16位模式/32位模式下PUSH指令探究——《x86汇编语言:从实模式到保护模式》读书笔记16
一.Intel 32 位处理器的工作模式 如上图所示,Intel 32 位处理器有3种工作模式. (1)实模式:工作方式相当于一个8086 (2)保护模式:提供支持多任务环境的工作方式,建立保护机制 ...
- 硬盘和显卡的访问与控制(一)——《x86汇编语言:从实模式到保护模式》读书笔记01
本文是<x86汇编语言:从实模式到保护模式>(电子工业出版社)的读书实验笔记. 这篇文章我们先不分析代码,而是说一下在Bochs环境下如何看到实验结果. 需要的源码文件 第一个文件是加载程 ...
- 程序的载入和运行(五)——《x86汇编语言:从实模式到保护模式》读书笔记25
程序的载入和运行(五)--<x86汇编语言:从实模式到保护模式>读书笔记25 前面几篇博文最终把代码分析完了.这篇就来说说代码的编译.运行和调试. 1.代码的编译及写入镜像文件 之前我们都 ...
- 存储器的保护(三)——《x86汇编语言:从实模式到保护模式》读书笔记20
存储器的保护(三) 修改本章代码清单,使之可以检测1MB以上的内存空间(从地址0x0010_0000开始,不考虑高速缓存的影响).要求:对内存的读写按双字的长度进行,并在检测的同时显示已检测的内存数量 ...
- 进入保护模式(三)——《x86汇编语言:从实模式到保护模式》读书笔记17
(十)保护模式下的栈 ;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作 mov cx,00000000000_11_000B ;加载堆栈段选择子 mov ss,cx mov esp,0x7c00 ...
- 进入保护模式(二)——《x86汇编语言:从实模式到保护模式》读书笔记14
首先来段题外话:之前我发现我贴出的代码都没有行号,给讲解带来不便.所以从现在起,我要给代码加上行号.我写博客用的这个插入代码的插件,确实不支持自动插入行号.我真的没有找到什么好方法,无奈之下,只能按照 ...
- 《X86汇编语言:从实模式到保护模式》读书笔记之引言
有幸结识了<X86汇编语言:从实模式到保护模式>一书.我觉得这本书非常好,语言活泼,通俗易懂,源码丰富,受益匪浅.读罢一遍,意犹未尽.于是打算再读一遍,并把自己的读书所学总结成笔记,一来给 ...
随机推荐
- JMS基本概念和模型
------------------------------------------------------------------------------------------- JMS是什么 J ...
- 利用Node.js对某智能家居服务器重构
原文摘自我的前端博客,欢迎大家来访问 http://www.hacke2.cn 之前负责过一个智能家居项目的开发,外包重庆一家公司的,我们主要开发服务器监控和集群版管理. 移动端和机顶盒的远程通信是用 ...
- edwin报警和监控平台近期的更新(python源码)
edwin从发布以来, 得到了不少关注, 获得了不少star. 最近又做了一些很有意义的改进, 同时完善了部分文档. 项目地址: https://github.com/harryliu/edwin , ...
- <s:url>指向的Action只执行一次,清除浏览器缓存文件后又可执行一次。
Action中的方法仅为静态变量赋值,而其他访问数据库的Action可以被重复执行. 起初判断可能是静态变量的内存机制导致不能重复执行. 然后发现清楚浏览器缓存文件后又可以执行一次了,看来原因在Jsp ...
- 常用开源镜像站整理android sdk manager
http://www.cocoachina.com/programmer/20151023/13852.html http://android-mirror.bugly.qq.com:8080/inc ...
- Javascript高级程序设计——面向对象之理解对象
在面向对象语言中都有类的概念,通过类来创建具有属性和方法的对象.而ECMAScript中没有类的概念,ECMAScript中定义了对象:无需属性的集合,其属性值可以包含基本值.对象.或者函数. 在Ja ...
- Javascript高级程序设计——基本概念(二)
相等操作符: 相等==:这个操作符会先转换操作数,强制类型转换,然后再比较他们的相等性. null == undefined //true NaN == NaN //false"5" ...
- FTP 传输中的主动模式和被动模式
最近做一个项目用到FTP和其它系统进行文件传输,结果在FTP网络连接的问题上花了很多时间,由于太久没搞多FTP,忘记了FTP不单单开放21端口,客户端采用不同连接模式对网络有不同.在此重温一下FTP的 ...
- String封装——读时共享,写时复制
碰到过一位一直怀疑C++标准库(STL)效率的人,他说STL效率太低,企业开发根本不会用.我是持反对意见的. 说这话的人,肯定没有做过大量的调查.没有调查就没有发言权. STL的效率是不低的,足够满足 ...
- PHP环境搭建——Apache、Mysql、PHP单独安装(for Windows)
提示: 安装之前先要安装vcredist_x86.exe或vcredist_x64.exe(vc6,vc9,vc11等,和下面对应). 确保apache和php是用同样版本的编译器编译出来的,如果是v ...