一、引言
本文参考 小林coding 的《图解操作系统》,也是我十分喜欢的一个公众号博主,为他打 call
老读者知道我之前再写 Kafka
的博文,为什么突然开始写操作系统的呢?
原因在于:
当我看到 Kafka
服务端的一些 IO 操作时,我发现我看不懂了,了解之后发现这里 Netty
的概念。
当我尝试了解 IO 时,我发现一些内存、磁盘的交换,搞的我焦头烂额,于是,想静下心来从头开始。
当我把 小林coding 的 《图解操作系统》看完之后,我发现对操作系统的理解更上一层楼。
用一段话,作为今天的开场白:
读书的根本目的,未必是解决现实问题,它更像一场心灵的抚慰。
一个喜欢读书的人,可能不会记得自己读过哪些书。
但是那些看过的故事、收获的感悟、浸染过的气质,就像一颗种子,会在你的身体里慢慢发芽长大,不断提升你的认知,打开你的视野。
二、图灵机的工作方式
简单讲述下图灵机的工作:
- 当我们的读写头读取我们的
1
和2
字符时,控制单元会将其读取到存储设备中。 - 当我们读写头读取到我们的
+
号时,控制单元发现此时的+
是一个运算符指令,于是通知 运算单元 进行工作。 运算单元 会将之前存储设备的数值取出并根据当前运算符指令得出最终的结果,存储到存储设备中。 - 最终形成如下的样子
三、冯诺依曼模型
定义计算机基本结构分为 5 个部分:运算器、控制器、存储器、输入设备、输出设备
运算器、控制器是在CPU里面的,存储器一般指内存,输入和输出设备指计算机外接的设备。
我们的内存和输入、输出设备和CPU打交道的话,离不开总线,关系如下:
1. 内存
程序和数据都是存储在内存中,存储单位为 bit
,最小的存储单位是 byte
。
内存的地址是从0开始编号的,然后自增排列,最后一个地址为内存总字节数 - 1。
2. 中央处理器(CPU)
2位CPU和64位CPU的区别(1字节 = 8 位):
- 32位可一次性计算 4 个字节
- 64位可一次性计算 8 个字节
CPU还有一些组件,比如:寄存器、控制单元、逻辑运算单元等。
- 控制单元:负责控制CPU工作
- 逻辑运算单元:负责计算
- 寄存器
- 通用寄存器:存储运算数据
- 程序计数器:用于存储CPU要执行下一条指令的内存地址
- 指令寄存器:存储指令
3. 总线
总线主要用于CPU和其他设备的通信
- 地执总线:CPU将要操作的内存地址
- 数据总线:读写内存的数据
- 控制总线:用于发送和接受信号,比如:中断
- 举个例子:
当CPU读写内存数据的时候:
- 首先要通过 地址总线 获取内存的地址
- 在通过 数据总线 传输数据
4. 输入、输出设备
输入设备向计算机输入数据,计算机经过计算后,把数据输出给输出设备。
如果输入设备是键盘,按下按键需要和CPU交互,需要发送中断信号,也就是要使用到我们的 控制总线
四、线路位宽和CPU位宽
一个位计算的数值:0、1
两个位计算的数值:00、01、10、11
…
32位CPU计算的数值:2 ^ 32
64位CPU计算的数值:2 ^ 64
五、程序执行的基本过程
程序实际上是一条条指令,负责执行指令的CPU将一条条指令一步步指令起来,形成程序的运行过程。
CPU的执行过程:
- CPU 读取 程序计数器 的值,找到下一条指令的内存地址,CPU 控制单元 操作地址总线访问内存地址,通知内存设备准备数据。数据准备好之后通过 数据总线 传给CPU,CPU收到内存传送数据之后,将其放入 指令寄存器。
- CPU 分析 指令寄存器 的指令,
- 确定指令的参数和类型。如果是计算类的指令,交于 逻辑运算单元 运算,如果是存储类的指令,交于 控制单元 执行。
- CPU 执行完指令之后,程序计数器 的值会自增,指向下一个指令地址。
- CPU指令周期:一个程序执行的时候,CPU 会根据程序计数器里的内存地址,从内存里面把需要执行的指令读取到指令寄存器里面执行,然后根据指令长度自增,开始顺序读取下一条指令。
六、a = 1 + 2 执行具体过程
CPU并不认识我们的 a = 1 + 2
这个字符串,需要将字符串转成计算机可以理解的 汇编语言。
我们简单翻译一下该字符串的汇编语言:
- 0x200:
load
指令将 0x100 地址的数据 1 装入到寄存器 R0 中 - 0x204:
load
指令将 0x104 地址的数据 2 装入到寄存器 R1 中 - 0x208:
add
指令将寄存器 R0 和 R1 的数据相加,并把结果放入到寄存器 R2 中。 - 0x20c:
store
指令将寄存器 R2 中的数据存回数据段中的 0x108 地址中,也就是变量a
所在的地址。
程序计数器一开始会指向 0x200 的第一条指令的位置,然后依次执行这 4 条指令。
1. 指令
每一条指令的内容是一串二进制数字的机器码,CPU 通过解析机器码来得知指令的内容。
指令一共有三种类型:
- R指令:算术和逻辑操作,读取和写入数据的寄存器的地址。
- I指令:数据传输、条件分支
- J指令:跳转操作
编译器在编译程序的时候,会构造指令,被称为:指令的编码
CPU 执行程序的时候,会进行解析指令,称为:指令的解码
现代CPU一条指令的执行通常为一个 指令周期,分为 4 个阶段:
- Fetch(获取指令):CPU 通过程序计数器读取内存地址的指令
- Decode(解析指令):CPU 对指令进行解析
- Execute(执行指令):CPU 执行指令
- Store(数据回写):CPU 将计算结果存回寄存器或者将寄存器的值存入内存
- 同的阶段由计算机不同的组件构成:
2. 指令的类型
指令从功能上划分:
- 数据传输的指令:
store/load
寄存器和内存的指令传输,mov
是将一个内存地址移动到另一个内存地址的指令 - 运算类型的指令:加减乘除等
- 跳转类型的指令:常见的
if-else
、switch-case
- 信号类型的指令:中断指令
- 闲置类型的指令:指令
nop
,执行后 CPU 会空转一个周期
3. 指令的执行速度
程序的 CPU 执行时间 = CPU 时钟周期数 * 时钟周期时间
程序的 CPU 执行时间 = 指令数 * 每条指令的平均时钟周期数 * 时钟周期时间
要想我们的程序跑的很快,优化三者即可:
- 指令数:表示执行程序需要多少条指令
- 每条指令的平均时钟周期数:表明一条指令需要多少个时钟周期数
- 时钟周期时间:表示计算机主频,取决于计算机硬件。比如当前计算机的主频为 1 GHz,指的是时钟频率是 1 G,代表着 1 秒会产生 1G 次数的脉冲信号,每一次脉冲信号高低电平的转换就是一个周期,称为时钟周期。所以,一个时钟周期 = 1 / 1G