I386的体系结构
第1章 内存寻址
曾经有一个叫“阿兰.图灵”的天才[1],他设想出了一种简单但运算能力几乎无限发达的理想机器——这可不是一个具体的机械玩艺,而是一个思想模型——用来计算能想象得到的所有可计算函数。这个有趣的机器由一个控制器、一个读写头和一条假设两端无限长的带子组成。工作带好比存储器,被划分成大小相同的格子,每格上可写一个字母,读写头可以在工作带上随意移动,而控制器可以要求读写头读取其下方工作带上的字母。
你可千万别觉得这个机器傻得可爱,它可是当代冯.诺依曼体系计算机的理论鼻祖。它带来的“数据连续存储和选择读取思想”正是目前我们使用的几乎所有机器运行背后的灵魂。计算机体系结构中的核心问题之一就是如何有效地进行内存寻址,因为所有运算的前提都是先要从内存中取得数据,所以内存寻址技术从某种程度上代表了计算机技术。
下面就开始一起聊聊关于寻址的故事。
冯.诺依曼体系计算机系统由运算器、存储器、控制器、输入设备、输出设备五大部件组成。运算器就是我们熟知的CPU中的ALU(算术逻辑单元),存储器是内存,控制器是CPU中的控制单元;输入设备就是我们的鼠标键盘等;输出设备就是显示器,打印机等。
1.1历史回顾
计算机的内存寻址技术和世界上的其它事物一样都经历了由简单到复杂,由笨拙到优雅的过程。自我听说计算机到今天,内存寻址方法发生了几次决定性的变革(“史前”的内存寻址方法我连资料都没有找到,真是无据可查了!),而每次变革都带来了软件技术的发展,注入了新鲜血液。
让我们沿着Intel公司的脚步来回顾一下历史吧!(我实在没机会接触除Intel以外的处理器!!!)
1.1.1石器时代
20年前intel推出了一款8位处理器——8080,它有1个主累加器(寄存器A)和6个次累加器(寄存器B,C,D,E,H和L),几个次累加器可以配对(如组成BC, DE或HL)使用来访问16位的内存地址,也就是说8080可访问到64K内的地址空间。另外那时还没有段的概念,访问内存都要通过绝对地址,因此程序中的地址必须进行硬编码,而且也难以重定位,故当时的软件大都是些可控性弱、结构简陋、数据处理量小的工控程序。
人类从来都是不断前进的,很快,几年后intel就开发出了16位的新处理器——8086,这便是内存寻址的第一次飞跃。
1.1.2 青铜时代
8086处理器引入了一个重要概念——段。段描述了一块有限的内存区域,区域的起始位置存在专门的寄存器(段寄存器)中。另外8086处理器可以寻址到1M大的内存空间,因为它的地址线扩展到了20位。可是制造20位的寄存器来存放内存地址在当时显然要比制造20位的地址线难得多。为了克服困难,intel的工程师们想出了个好办法:将内存分为数个64k大小的段,然后利用两个16位值——一个是段地址,另一个是段内偏移量——巧妙组合产生20位的内存地址。换句话说就是把1M大的空间分成数个64k的段来管理(化整为零了)。
系统所需要做的仅仅是:把16位的段地址左移动4位后,再与16位的偏移量相加便可获得一个20位的内存地址,见图1
段基址
偏移
物理地址
图 1
Intel内存地址的描述形式也很贴近上图,采用了“段地址:偏移量”的形式来描述内存地址,比如A815:CF2D就代表段首地址在A815,段内偏移位CF2D。
为了支持段机制,8086为程序使用的代码段,数据段,堆栈段分别提供了专门的16位寄存器CS,DS和SS,此外还给内存和字符串拷贝操作留下了一个目的段寄存器:ES。
段式内存管理带来了显而易见的优势——程序的地址不再需要硬编码了,调试错误也更容易定位了,更可贵的是支持更大的内存地址。程序员开始获得了自由。
1.1.3白银时代
人们的欲望在继续膨胀。intel的80286处理器于1982年问世了,它的地址总线位数增加到了24位,因此可以访问到16M的内存空间。更重要的是从此开始引进了一个全新理念——保护模式。这种模式下内存段的访问受到了限制。访问内存时不能直接从段寄存器中获得段的起始地址了,而需要经过额外转换和检查(从此你不能再随意执行数据段,或向代码段里写东西了,其具体保护和实现我们将在后面讲述)。
第1章 内存寻址
曾经有一个叫“阿兰.图灵”的天才[1],他设想出了一种简单但运算能力几乎无限发达的理想机器——这可不是一个具体的机械玩艺,而是一个思想模型——用来计算能想象得到的所有可计算函数。这个有趣的机器由一个控制器、一个读写头和一条假设两端无限长的带子组成。工作带好比存储器,被划分成大小相同的格子,每格上可写一个字母,读写头可以在工作带上随意移动,而控制器可以要求读写头读取其下方工作带上的字母。
你可千万别觉得这个机器傻得可爱,它可是当代冯.诺依曼体系计算机的理论鼻祖。它带来的“数据连续存储和选择读取思想”正是目前我们使用的几乎所有机器运行背后的灵魂。计算机体系结构中的核心问题之一就是如何有效地进行内存寻址,因为所有运算的前提都是先要从内存中取得数据,所以内存寻址技术从某种程度上代表了计算机技术。
下面就开始一起聊聊关于寻址的故事。
冯.诺依曼体系计算机系统由运算器、存储器、控制器、输入设备、输出设备五大部件组成。运算器就是我们熟知的CPU中的ALU(算术逻辑单元),存储器是内存,控制器是CPU中的控制单元;输入设备就是我们的鼠标键盘等;输出设备就是显示器,打印机等。
1.1历史回顾
计算机的内存寻址技术和世界上的其它事物一样都经历了由简单到复杂,由笨拙到优雅的过程。自我听说计算机到今天,内存寻址方法发生了几次决定性的变革(“史前”的内存寻址方法我连资料都没有找到,真是无据可查了!),而每次变革都带来了软件技术的发展,注入了新鲜血液。
让我们沿着Intel公司的脚步来回顾一下历史吧!(我实在没机会接触除Intel以外的处理器!!!)
1.1.1石器时代
20年前intel推出了一款8位处理器——8080,它有1个主累加器(寄存器A)和6个次累加器(寄存器B,C,D,E,H和L),几个次累加器可以配对(如组成BC, DE或HL)使用来访问16位的内存地址,也就是说8080可访问到64K内的地址空间。另外那时还没有段的概念,访问内存都要通过绝对地址,因此程序中的地址必须进行硬编码,而且也难以重定位,故当时的软件大都是些可控性弱、结构简陋、数据处理量小的工控程序。
人类从来都是不断前进的,很快,几年后intel就开发出了16位的新处理器——8086,这便是内存寻址的第一次飞跃。
1.1.2 青铜时代
8086处理器引入了一个重要概念——段。段描述了一块有限的内存区域,区域的起始位置存在专门的寄存器(段寄存器)中。另外8086处理器可以寻址到1M大的内存空间,因为它的地址线扩展到了20位。可是制造20位的寄存器来存放内存地址在当时显然要比制造20位的地址线难得多。为了克服困难,intel的工程师们想出了个好办法:将内存分为数个64k大小的段,然后利用两个16位值——一个是段地址,另一个是段内偏移量——巧妙组合产生20位的内存地址。换句话说就是把1M大的空间分成数个64k的段来管理(化整为零了)。
系统所需要做的仅仅是:把16位的段地址左移动4位后,再与16位的偏移量相加便可获得一个20位的内存地址,见图1
段基址
偏移
物理地址
图 1
Intel内存地址的描述形式也很贴近上图,采用了“段地址:偏移量”的形式来描述内存地址,比如A815:CF2D就代表段首地址在A815,段内偏移位CF2D。
为了支持段机制,8086为程序使用的代码段,数据段,堆栈段分别提供了专门的16位寄存器CS,DS和SS,此外还给内存和字符串拷贝操作留下了一个目的段寄存器:ES。
段式内存管理带来了显而易见的优势——程序的地址不再需要硬编码了,调试错误也更容易定位了,更可贵的是支持更大的内存地址。程序员开始获得了自由。
1.1.3白银时代
人们的欲望在继续膨胀。intel的80286处理器于1982年问世了,它的地址总线位数增加到了24位,因此可以访问到16M的内存空间。更重要的是从此开始引进了一个全新理念——保护模式。这种模式下内存段的访问受到了限制。访问内存时不能直接从段寄存器中获得段的起始地址了,而需要经过额外转换和检查(从此你不能再随意执行数据段,或向代码段里写东西了,其具体保护和实现我们将在后面讲述)。