《深入浅出计算机组成原理》学习笔记 Day2

指令篇

1. 从高级语言到机器指令

计算机或者说CPU本身并没有能力去理解这些高级语言,其只能识别由0、1组成的机器语言。

1.1 CPU的作用

CPU 是计算机的大脑。全称是 Central Processing Unit,即中央处理器。

从硬件角度来看,CPU 就是一个超大规模集成电路,通过电路实现了加法、乘法乃至各种各样的处理逻辑。
从软件工程师的角度来讲,CPU 就是一个执行各种计算机指令的逻辑机器。这些计算机指令也就是机器语言。

不同的CPU 能够听懂的语言不太一样,这些“语言”也就是计算机指令集(Instruction Set)。

一个计算机程序,不可能只有一条指令,而是由成千上万条指令组成的。但是 CPU 里不能一直放着所有指令,所以计算机程序平时是存储在存储器中的。这种程序指令存储在存储器里面的计算机,我们就叫作存储程序型计算机(Stored-program Computer)。

1.2 代码如何变为机器码

在这里插入图片描述
程序首先被翻译成一个汇编语言,这个过程叫编译(Compile)。
然后针对汇编代码,再用汇编器翻译成机器码,这一条条机器码,就是一条条的计算机指令

1.3 指令的分类

常见指令可以分为五大类:

  1. 算术类指令。我们的加减乘除,在CPU 层面都会变成一条条算术类指令。
  2. 数据传输类指令。给变量赋值、在内存里读写数据,用的都是数据传输类指令。
  3. 逻辑类指令。逻辑上的与或非,都是这一类指令。
  4. 条件分支类指令。日常我们写的if / else,都是条件分支类指令。
  5. 无条件跳转指令。在调用函数的时候,其实就是发起了一个无条件跳转指令。

在这里插入图片描述

2. 指令跳转

2.1 CPU 是如何执行指令

逻辑上,我们可以认为,CPU 其实就是由寄存器组成的。二寄存器就是 CPU 内部,由多个触发器或者锁存器组成的简单电路。

在这里插入图片描述
常见的三种寄存器:

  1. PC寄存器(Program Counter Register)。也叫指令地址寄存器。起作用就是用来存放下一条需要执行的计算机指令的内存地址。
  2. 指令寄存器(Instruction Register)。用来存放当前正在执行的指令。
  3. 条件码寄存器(Status Register)。用里面的一个个标记位(Flag)来存放 CPU 进行算术或者逻辑计算的结果。

除了这些特殊的寄存器,CPU 里面还有更多用来存储数据和内存地址的寄存器。这样的寄存器通常一类里面不止一个。我们通常根据存放的数据内容来给它们取名字,比如整数寄存器、浮点数寄存器、向量寄存器和地址寄存器等等。有些寄存器既可以存放数据,又能存放地址,我们就叫它通用寄存器。

实际上,一个程序执行的时候,CPU 会根据 PC 寄存器里的地址,从内存里面把需要执行的指令读取到指令寄存器里面执行,然后根据指令长度自增,开始顺序读取下一条指令。可以看到,一个程序的一条条指令,在内存里面是连续保存的,也会一条条顺序加载。

而跳转指令会修改 PC 寄存器里面的地址值。这样,下一条要执行的指令就不是从内存里面顺序加载的了。

2.2 条件和循环的本质

除了简单地通过 PC 寄存器自增的方式顺序执行外,条件码寄存器会记录下当前执行指令的条件判断状态,然后通过跳转指令读取对应的条件码,修改 PC 寄存器内的下一条指令的地址,最终实现 if…else 以及 for/while 这样的程序控制流程。

if…else…、for/wihile本质上都是和goto相同的跳转到特定指令位置的方式来实现的。

想要在硬件层面实现这个 goto 语句,除了本身需要用来保存下一条指令地址,以及当前正要执行指令的 PC 寄存器、指令寄存器外,只需要再增加一个条件码寄存器,来保留条件判断的状态。这样简简单单的三个寄存器,就可以实现条件判断和循环重复执行代码的功能。

3. 函数调用

3.1 栈的作用

栈是一种后进先出(LIFO)的数据结构,是内存的一段空间。栈就像一个乒乓球桶,每次程序调用函数之前,我们都把调用返回后的地址写在一个乒乓球上,然后塞进这个球桶。这个操作其实就是我们常说的压栈。如果函数执行完了,我们就从球桶里取出最上面的那个乒乓球,很显然,这就是出栈

整个函数所占用的内存空间,就是函数的栈帧(Stack Frame)。rbp 是 register base pointer 栈基址寄存器(栈帧指针),指向当前栈帧的栈底地址。rsp 是 register stack pointer 栈顶寄存器(栈指针),指向栈顶元素。

通过加入了栈,相当于在指令跳转的过程中,加入了一个“记忆”的功能,能在跳转去运行新的指令之后,再回到跳出去的位置,能够实现更加丰富和灵活的指令执行流程。

3.2 Stack Overflow

通过引入栈,可以看到,无论有多少层的函数调用,或者在函数 A 里调用函数 B,再在函数 B 里调用 A,这样的递归调用,我们都只需要通过维持 rbp 和 rsp,这两个维护栈顶所在地址的寄存器,就能管理好不同函数之间的跳转。不过,栈的大小也是有限的。如果函数调用层数太多,我们往栈里压入它存不下的内容,程序在执行的过程中就会遇到栈溢出的错误,这就是“stack overflow”。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Balaaam

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值