一:对程序员来说CPU是什么?

 0、开篇

   (1)程序是什么?
         指示计算机每一步动作的一组指令
    (2)程序是由什么组成的?
         指令和数据
    (3)什么是机器语言?
         CPU可以直接识别并使用的语言
    (4)正在运行的程序存储在什么位置?
         内存
    (5)什么是内存地址?
         内存中,用来表示命令和数据存储位置的数值
    (6)计算机的构成元件中,负责程序的解释和运行是哪个?
        CPU
 

1、CPU的内部结构解析

    ① CPU所负责的就是解释和运行最终转换成机器语言的程序内容。如下图所示:
    
 
    ② CPU的内部由寄存器、控制器、运算器和时钟四个部分构成,各部分之间由电流信号相互连通。
    寄存器:暂存指令、数据等处理对象;
    控制器:把内存上的指令、数据等读入寄存器;
    运算器:运算从内存读入寄存器的数据;
    时钟:发出CPU开始计时的时钟信号;(时钟信号的频率越高,CPU的运行速度就越快)
    
 
    ③ 内存,通过控制芯片与CPU相连,主要负责存储指令和数据。内存由可读写的元素构成,每个字节(1字节=8位)都带有一个地址编号。CPU可以通过该地址读取主存中的指令和数据,当然也可以写入数据。
Warn:内存中存储的指令和数据会随着计算机的关机而自动清除。因为其通常使用DRAM(Dynamic Random Access Memory,动态随机存取存储器)芯片。
 
    ④ 通过了解CPU的构造后,程序的运行机制大概如下:
程序启动后,根据时钟信号,控制器会从内存中读取指令和数据。通过对这些指令加以解释和运行,运算器就会对数据进行运算,控制器根据该运算结果来控制计算机。

2、CPU是寄存器的集合体

    ① CPU的四个构成部分中,程序员只需要了解寄存器即可。因为程序是把寄存器作为对象来描述的。
不同类型的CPU,其内部寄存器的数量、种类以及寄存器存储的数值范围都是不同的。不过,根据功能的不同,我们可以将寄存器大致分为8类,如下图:
    
    可以看出,寄存器中存储的内容既可以是指令也可以是数据。其中数据分为“用于运算的数值”和“表示内存地址的数值”两种。它们使用的寄存器也是不同的。用于运算的数值放在累加寄存器中存储,表示内存地址的数值则放在基址寄存器和变址寄存器中存储。
 
    ② 对程序员来说,CPU就是各种功能的寄存器的集合体。其中,程序计数器、累加寄存器、标志寄存器、指令寄存器和栈寄存器都只有一个,其他的寄存器一般有多个。
如下图所示,程序员眼中的CPU
   
 

3、决定程序流程的程序计数器

    存储指令和数据的内存,是通过地址来划分的:
   
    上图可知:程序计数器决定这程序的流程,实际上,一个命令和数据通常被存储在多个地址上,上图只是为了便于说明,把指令、数据分配到了一个地址中。
 

4、条件分支和循环分支

    ① 程序的流程一般有三种:顺序执行、条件执行和循环三种。
顺序执行的情况比较简单,每执行一个指令程序计数器的值就自动加1;但若程序中存在条件分支和循环,机器语言的指令就可以将程序计数器的值设定为任意地址。
    下图会以条件分支为例:
     
    程序运行的开始位置是0100地址。随着程序计数器数值的增加,当达到0102地址时,如果累加寄存器的值是正数,则执行跳转指令(jump指令)跳转到0104地址。此时,由于累加寄存器的值时123,为正数,因为0103地址的指令被跳过,程序的流程直接跳转到了0104地址。也就是说,“跳转到0104地址”这个指令间接执行了“将程序计数器设定为0104地址”这个操作。
 
    ② 条件分支和循环分支使用的跳转指令,会参照当前执行的运算结果。结果是存储在标志寄存器中的(也负责存放溢出和奇偶校验的结果)
    

    32位CPU(寄存器的长度是32位)

    标志寄存器的数值会根据运算结果自动设定。至于是否执行跳转指令,则由CPU在参考标志寄存器的数值后进行判断。运算结果的正、零、负三种状态由标志寄存器的三个位表示,标志寄存器的第一个字节位、第二个字节位和第三个字节位的值位1时,表示运算结果分别为正数、零和负数。

5、函数的调用机制

    函数调用处理也是通过把程序计数器的值设定成函数的存储地址来实现的。不过,这和条件分支、循环的机制所有不同,因为单纯的跳转指令无法实现函数的调用。函数的调用需要在完成函数内部的处理后,处理流程再返回到函数调用点(函数调用指令的下一个地址)。因此,如果只是跳转到函数的入口地址,处理流程就不知道应该返回至哪里去。
    鉴于这个问题,机器语言的call指令和return指令能够解决这个问题。
    在将函数的入口地址设定到程序计数器之前,call指令会把调用函数后要执行的指令地址存储在名为栈的主存内。函数处理完毕后,再通过函数的出口来执行return命令。
    return命令的功能是把保存在栈中的地址设定到程序计数器中。
    

    

    在编译高级编程语言的程序后,函数调用的处理会转换成call指令,函数结束的处理则会转换成return指令。这样一来程序的运行也就变得非常流畅。

6、通过地址和索引实现数组

    这一小节就要说到基址寄存器和变址寄存器了。通过这两个寄存器,我们可以对主内存上特定的内存区域进行划分,从而实现类似于数组的操作。
    首先,我们用十六进制数将计算机内存上00000000~FFFFFFFF的地址划分出来。那么,凡是该范围的内存区域,只要有一个32位的寄存器,即可查看全部的内存地址。但如果想要像数组那样分割特定的内存区域以达到连续查看的目的,使用两个寄存器会更方便。如下图所示:
    

    假设要查看10000000~1000FFFF地址时,如上图所示,可以将1000000存入基址期存器,并使变址寄存器的值在00000000~0000FFFF变化,CPU则会把基址寄存区+变址寄存器的值解释为实际查看的内存地址。

7、CPU的处理其实很简单

    CPU可以进行的处理不多,下图列举一些:

    

 

posted @ 2017-06-18 23:21  Helius-黑牛  阅读(1508)  评论(0编辑  收藏  举报