一. 冯诺依曼体系结构
冯诺依曼是计算机领域的祖师爷, 被评为"二十一世纪最伟大的"全才""
冯诺依曼提出了冯诺依曼体系结构, 定义了一台计算机, 由这几部分构成:
输入设备: 包括键盘, 鼠标, 摄像头…
CPU: 控制器 + 运算器
存储器: 内存 + 外存
输出设备: 显示器, 音箱, 打印机…
二. CPU
打开任务管理器, 我们可以看到:
- 6%: 表示CPU的占用率
CPU就像一个打工人, 一个打工人, 一天可能工作8个小时, 领导给她安排一些活, 这个打工人花了4个小时就干完了, 剩下4个小时就可以摸鱼了, 此时cpu的占用率就是50% - 3.06GHz: 表示CPU的频率, 翻译过来3.06GHz == 30亿 Hz, 简单认为一秒钟执行30亿条指令
它描述了CPU的干活效率, 数字越大, 干活就越快, 可以简单粗暴理解为:一秒钟, cpu运行多少次/执行多少条指令(不够严谨)
cpu的频率有两个概念:
基础频率:保底多少
最大频率(最大加速睿频): 拉满多少
实际上, 现在的CPU都是**“多核心CPU”**, 一个CPU其实是好几个打工人的集合
另外intel(搞CPU的两大巨头, 另一个是AMD), 研究了"超线程技术", 让一个CPU核心, 当两个用
例如我的电脑, 就是4核(物理核心) 8线程(逻辑核心)
三. 指令
指令, 可以认为是CPU被设计时, 给程序猿提供的一些"程序接口", 近似理解为cpu提供给程序猿的API
不同的cpu,提供的指令是不同的
但是虽然不相同, 但是有几套标准的:
- x86架构
最主流的一套标准, x86架构的cpu
intel/AMD 生产的 pc / 服务器 都属于x86系列的cpu
由来:
上个世纪70年代左右, intel发布了一系列 8086 cpu, 80286, 80386…非常经典, 支持的指令和内部构造大同小异, 称为x86 系列
- arm架构
用于手机, 平板, 各种嵌入式设备, 例如苹果的M1芯片, M2芯片…
*四. CPU是如何执行指令的
指令, 都需要先加载到内存中, 然后才能被CPU读取, 并执行
冯诺依曼体系结构规定:
一个程序, 指令和依赖的数据, 都是要在存储器中保存的, CPU如果想要执行, 都要自动的从存储器中读取到对应的指令
例如: 一个java程序, 写出来的是一个.java源代码文件, 经过编译, 得到.class二进制文件(仍然存储在硬盘上), 再经过运行, jvm就会把这个.class文件加载到内存中, 再进一步翻译成CPU能识别的二进制文件
接下来, CPU执行程序, 大概经历了三个阶段:
- 读指令
把内存中的指令数据, 读取到CPU的寄存器中
寄存器: CPU上也能存储数据, 存数据的模块就叫寄存器, 速度比内存快, 但是存储空间更小, 更贵 - 解析指令
- 执行指令
注意上述只是一个简述的阶段, 实际上CPU会更复杂
每个CPU被设计出来都会提供吧这样的指令表, 供CPU来解析指令
RAM: 内存
此处一个指令时8位二进制数字, 前四位用来区分这是什么指令, 后四位数操作数, 不同的指令不一样的作用
实际上的CPU一条指令, 不仅仅是八位, 但大体上也是 操作码 + 操作数 构成的
假设内存中的数据如下图:
设这段数据描述了一段程序, 并从0号地址开始执行, 想要理解这段程序, 就需要结合指令表, 至于哪些是指令, 哪些是数据, 是看不出来的, 只有在程序跑起来之后才能知道(都是系统安排好的)
cpu中有一个专门的寄存器, 保存着要从哪个内存地址来取指令, 叫做"程序计数器", 简称 “pc”
我们假设从0号开始执行, CPU每次读取执行完一条指令后, 就会自动把pc中的值 +1 , 顺序执行下一条指令, (如果遇到跳转指令, 就不是 + 1, 而是被设置成特定的地址)
- 第一轮操作
- 取地址: cpu取出0号地址的指令
2 )解析指令: 去指令表中查询, 看指令是什么意思
操作码:
查询指令表, 发现对应LOAD_A指令, 要从后面跟着的地址上读取数据到A寄存器中
操作数:
要操作的数的地址是1110 => 14 - 执行指令: 把14地址的数据取出来, 放在A寄存器中
00000011 => 3, 把3存在A寄存器中
- 第二轮操作
- 取指令
由于刚刚pc = 0, 顺延应该pc++, pc = 1, 取1号地址的指令
- 解析指令
操作码:
查询指令表, , 发现对应LOAD_B指令, 要从后面跟着的地址上读取数据到B寄存器中
操作数:
要操作的数的地址是1111 => 15 - 执行指令
把15地址的数据取出来, 00001110 => 14, 把14放在B寄存器中
- 第三轮操作
- 取指令
取2号地址指令
- 解析指令
操作码:
查询指令表, 发现对应ADD指令, 此时后面的操作数, 就是寄存器的编号(已知00 =>A 01 =>B)
操作数:
01对应B寄存器, 00对应A寄存器, 将这两个寄存器中的数据相加, 相加的和存到第二个寄存器中, 也就是A寄存器 - 执行指令
A中为3, B中为14, 3+14 = 17, 将17存到A中 此时A:17, B: 14
- 第四轮操作
1)取指令
取3号地址指令
2)解析指令
操作码:
对应指令表STORE_A指令, 把A寄存器的数据写入后面的地址中
操作数:
1101 => 13
3)执行指令
把A寄存器中的数据17 => 00010001 放在13号地址中 - 第五轮操作
1)取指令
取4号地址的数据
2)解析指令
指令表中没有这中操作, 一般就表示程序运行完毕, 一般为全0
3)执行指令
程序就退出了