-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathch17s02.html
3 lines (3 loc) · 9.1 KB
/
ch17s02.html
1
2
3
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>2. CPU</title><link rel="stylesheet" href="styles.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="Linux C编程一站式学习" /><link rel="up" href="ch17.html" title="第 17 章 计算机体系结构基础" /><link rel="prev" href="ch17s01.html" title="1. 内存与地址" /><link rel="next" href="ch17s03.html" title="3. 设备" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">2. CPU</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="ch17s01.html">上一页</a> </td><th width="60%" align="center">第 17 章 计算机体系结构基础</th><td width="20%" align="right"> <a accesskey="n" href="ch17s03.html">下一页</a></td></tr></table><hr /></div><div class="sect1" lang="zh-cn" xml:lang="zh-cn"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="id2766148"></a>2. CPU</h2></div></div></div><p>CPU总是周而复始地做同一件事:从内存取指令,然后解释执行它,然后再取下一条指令,再解释执行。CPU最核心的功能单元包括:</p><div class="itemizedlist"><ul type="disc"><li><p>寄存器(Register)<a id="id2766167" class="indexterm"></a>,是CPU内部的高速存储器,像内存一样可以存取数据,但比访问内存快得多。随后的几章我们会详细介绍x86的寄存器<code class="literal">eax</code>、<code class="literal">esp</code>、<code class="literal">eip</code>等等,有些寄存器只能用于某种特定的用途,比如<code class="literal">eip</code>用作程序计数器,这称为特殊寄存器(Special-purpose Register)<a id="id2766202" class="indexterm"></a>,而另外一些寄存器可以用在各种运算和读写内存的指令中,比如<code class="literal">eax</code>寄存器,这称为通用寄存器(General-purpose Register)<a id="id2766217" class="indexterm"></a>。</p></li><li><p>程序计数器(PC,Program Counter)<a id="id2766230" class="indexterm"></a>,是一种特殊寄存器,保存着CPU取下一条指令的地址,CPU按程序计数器保存的地址去内存中取指令然后解释执行,这时程序计数器保存的地址会自动加上该指令的长度,指向内存中的下一条指令。</p></li><li><p>指令译码器(Instruction Decoder)<a id="id2766246" class="indexterm"></a>。CPU取上来的指令由若干个字节组成,这些字节中有些位表示内存地址,有些位表示寄存器编号,有些位表示这种指令做什么操作,是加减乘除还是读写内存,指令译码器负责解释这条指令的含义,然后调动相应的执行单元去执行它。</p></li><li><p>算术逻辑单元(ALU,Arithmetic and Logic Unit)。如果译码器将一条指令解释为运算指令,就调动算术逻辑单元去做运算,比如加减乘除、位运算、逻辑运算。指令中会指示运算结果保存到哪里,可能保存到寄存器中,也可能保存到内存中。</p></li><li><p>地址和数据总线(Bus)<a id="id2766284" class="indexterm"></a>。CPU和内存之间用地址总线、数据总线和控制线连接起来,每条线上有1和0两种状态。如果在执行指令过程中需要访问内存,比如从内存读一个数到寄存器,执行过程可以想像成这样:</p><div class="figure"><a id="id2766295"></a><p class="title"><b>图 17.2. 访问内存读数据的过程</b></p><div class="figure-contents"><div><img src="images/arch.readmem.png" alt="访问内存读数据的过程" /></div></div></div><br class="figure-break" /><div class="orderedlist"><ol type="1"><li><p>CPU内部将寄存器对接到数据总线上,使寄存器的每一位对接到一条数据线,等待接收数据。</p></li><li><p>CPU通过控制线发一个读请求,并且将内存地址通过地址线发给内存。</p></li><li><p>内存收到地址和读请求之后,将相应的内存单元对接到数据总线的另一端,这样,内存单元每一位的1或0状态通过一条数据线到达CPU寄存器中相应的位,就完成了数据传送。</p></li></ol></div><p>往内存里写数据的过程与此类似,只是数据线上的传输方向相反。</p></li></ul></div><p>上图中画了32条地址线和32条数据线,CPU寄存器也是32位,可以说这种体系结构是32位的,比如x86就是这样的体系结构,目前主流的处理器是32位或64位的。地址线、数据线和CPU寄存器的位数通常是一致的,从上图可以看出数据线和CPU寄存器的位数应该一致,另外有些寄存器(比如程序计数器)需要保存一个内存地址,因而地址线和CPU寄存器的位数也应该一致。处理器的位数也称为字长,字(Word)<a id="id2766367" class="indexterm"></a>这个概念用得比较混乱,在有些上下文中指16位,在有些上下文中指32位(这种情况下16位被称为半字Half Word<a id="id2766376" class="indexterm"></a>),在有些上下文中指处理器的字长,如果处理器是32位那么一个字就是32位,如果处理器是64位那么一个字就是64位。32位计算机有32条地址线,地址空间(Address Space)<a id="id2766387" class="indexterm"></a>从0x00000000到0xffffffff,共4GB,而64位计算机有更大的地址空间。</p><p>最后还要说明一点,本节所说的地址线、数据线是指CPU的内总线,是直接和CPU的执行单元相连的,内总线经过MMU和总线接口的转换之后引出到芯片引脚才是外总线,外地址线和外数据线的位数都有可能和内总线不同,例如32位处理器的外地址总线可寻址的空间可以大于4GB,到<a class="xref" href="ch17s04.html#arch.mmu">第 4 节 “MMU”</a>再详细解释。</p><p>我们结合<a class="xref" href="intro.program.html#intro.instruction">表 1.1 “一个语句的三种表示”</a>看一下CPU取指执行的过程。</p><div class="figure"><a id="id2766429"></a><p class="title"><b>图 17.3. CPU的取指执行过程</b></p><div class="figure-contents"><div><img src="images/arch.von.png" alt="CPU的取指执行过程" /></div></div></div><br class="figure-break" /><div class="orderedlist"><ol type="1"><li><p><code class="literal"><code class="literal">eip</code>寄存器指向地址0x80483a2,CPU从这里开始取一条5个字节的指令,然后<code class="literal">eip</code>寄存器指向下一条指令的起始地址0x80483a7。</code></p></li><li><p>CPU对这5个字节译码,得知这条指令要求从地址0x804a01c开始取4个字节保存到<code class="literal">eax</code>寄存器。</p></li><li><p>执行指令,读内存,取上来的数是3,保存到<code class="literal">eax</code>寄存器。注意,地址0x804a01c~0x804a01f里存储的四个字节不能按地址从低到高的顺序看成0x03000000,而要按地址从高到低的顺序看成0x00000003。也就是说,对于多字节的整数类型,低地址保存的是整数的低位,这称为小端(Little Endian)<a id="id2766497" class="indexterm"></a>字节序(Byte Order)<a id="id2766505" class="indexterm"></a>。x86平台是小端字节序的,而另外一些平台规定低地址保存整数的高位,称为大端(Big Endian)<a id="id2766514" class="indexterm"></a>字节序。</p></li><li><p>CPU从<code class="literal">eip</code>寄存器指向的地址取一条3个字节的指令,然后<code class="literal">eip</code>寄存器指向下一条指令的起始地址0x80483aa。</p></li><li><p>CPU对这3个字节译码,得知这条指令要求把<code class="literal">eax</code>寄存器的值加1,结果仍保存到<code class="literal">eax</code>寄存器。</p></li><li><p>执行指令,现在<code class="literal">eax</code>寄存器中的数是4。</p></li><li><p>CPU从<code class="literal">eip</code>寄存器指向的地址取一条5个字节的指令,然后<code class="literal">eip</code>寄存器指向下一条指令的起始地址0x80483af。</p></li><li><p>CPU对这5个字节译码,得知这条指令要求把<code class="literal">eax</code>寄存器的值保存到从地址0x804a018开始的4个字节。</p></li><li><p>执行指令,把4这个值保存到从地址0x804a018开始的4个字节(按小端字节序保存)。</p></li></ol></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="ch17s01.html">上一页</a> </td><td width="20%" align="center"><a accesskey="u" href="ch17.html">上一级</a></td><td width="40%" align="right"> <a accesskey="n" href="ch17s03.html">下一页</a></td></tr><tr><td width="40%" align="left" valign="top">1. 内存与地址 </td><td width="20%" align="center"><a accesskey="h" href="index.html">起始页</a></td><td width="40%" align="right" valign="top"> 3. 设备</td></tr></table></div></body></html>