本文主要是从程序运行来看计算机组成原理
我们平时常说的程序, 大多指的是一个可执行文件 .exe, 当双击 .exe文件运行时, 操作系统就会读取这个文件, 将其加载到内存中, 由CPU中的寄存器读取相应指令, 运算后返回内存
Java代码也是同理.
写了一个 .java 被编译成 .class 后被 JVM 进行加载, .class中保存的就是一些二进制指令 (和CPU无关), 也就会先放在内存中
1 计算机的大脑----CPU:中央处理器
1.1 简介
- 芯片:主要指的就是CPU
在计算机中,机组,微机原理接口技术,计算机系统结构,模电,数电…都是在讲CPU的,这几门课是计算机的基础
1.1.1 引入: CPU与显卡的区别
- CPU:: 通用计算结构 -> 应对各种计算场景
- 相当于大学生, 参加工作, 可以从事各种工作, 当然能计算1+1
- 显卡: 专用计算芯片 -> 特别擅长大量的简单运算, 如: 大规模浮点数, 尤其是矩阵运算) - 相当于小学生, 只会算1+1,但有些场景下需要运算大量的1+1
- 游戏: 画面非常复杂, 需要很大运算量
- 挖矿: 本质是狂算 哈希值, 相当于把自己电脑的算力贡献出来, 替别人完成一些简单运算, 当你的电脑算力为其他人提供了实质性帮助时, 他人就会给你一定报酬, 这个过程成为挖矿
- 机器学习: 模型的训练, 也需要巨大的运算量 (如: 对庞大数据进行分析等)
1.1.2 一些基本参数
- 1.90GHz : CPU的主频, 描述了CPU的运算速度
- Hz: 一秒震动 (运算) 一次
- G: 1G ≈10亿
1.2 内部结构
1.2.1 本质: 一系列的门电路
半加器 (2bits输入, 2bits输出) + 全加器 (3bits输入, 2bits输出) -> 各种加法器 -> 各种门电路 -> 计算单元 / 控制单元/ 存储单元… => CPU
1.2.1.1 CPU内部结构的发展方向
底层设备结构的变化, 对于程序员的代码编写方式, 也是有深远影响的
1.2.1.1.1 制程 越来越小
-
什么是制程?
计算机芯片框架的运算速度
- 运算速度: 每秒处理的百万级的机器语言指令数. 微机一般用主频来描述运算速度. 主频越高, 运算速度越快 -
为什么我们认为CPU的 制程 越小越好?
一个CPU芯片的面积是固定的,制程越小,单个门电路体积越小, 整个CPU上能够搭载的门电路数量越多, CPU上的功能模块越多, 算的就更快
但从物理角度来说, 制程是无法无限小的, 纵使无限小, 在nm尺度上, 经典力学已经处在失效的边缘了. 目前就小制程CPU的研究方向于 “量子计算机”, 它的计算能力会比经典计算机提升好多个数量级
1.2.1.1.2 多核CPU
家用PC上, 好的CPU, 12核24线程 #并发编程
如果在服务器的CPU上, 核心数会更多
1.2.2 寄存器
- 功能: 存储程序运行中的数据
- 和内存相比, 功能差不多, 数据断电就消失; 同时, 存储空间更小, 访问速度更快 (比内存又快3-4个数量级)
- CPU的计算是针对寄存器中的值进行的, 辅助计算, 保存计算中所需要的中间结果
- 示例: 计算10+20
- 从 内存 中读 10 到 寄存器A
- 从 内存 中读 20 到 寄存器B
- 执行相加指令, 把两个寄存器的值相加, 保存到 寄存器A 中
- 把 寄存器A 中的值写回内存
- 示例: 计算10+20
- 大小: 一般就是几百个字节, 最多几K
1.3 CPU如何进行工作
1.3.1 指令: 人给计算机发布的命令
1.3.1.1 人与计算机的交流
-
人写的: 高级语言
- Java/C 等编程语言构成的代码
-
计算机认识的: 机器语言
- 二进制的指令
- [[attachment/未命名#^326d9f|指令表]] (极简版本, 实际上指令有很多)
- 二进制的指令
-
编译: 把 高级语言 翻译成 机器语言
1.3.1.2 CPU执行一条指令的过程
- 从程序计数器中获取要读的指令, 再去内存中读取指令
1.3.1.2.1 程序计数器(PC指针)
在x86_32CPU叫做eip寄存器
程序计数器中包含了一个"地址", 指示接下来要读取哪个内存里的指令
-
解析指令
- 按照设定进行解读. 如: 一个8bit 指令, 前4bit 是其操作码(opcode), 表示指令要干什么; 后4bit 是操作数
- 指令00101110 => 从11010这个地址中读取一个字节进到A寄存器中
- 0010 Load_A
- 1110 要读取的内存地址
- 指令00101110 => 从11010这个地址中读取一个字节进到A寄存器中
- 按照设定进行解读. 如: 一个8bit 指令, 前4bit 是其操作码(opcode), 表示指令要干什么; 后4bit 是操作数
-
执行指令
然后, 程序计数器 进行累加, 再去读取下一条指令(针对单纯的顺序语句)
1.3.1.3 CPU如何执行多条指令: “流水线”
负责读指令的程序计数器读完第一条, 紧接着读第二条, 第三条…
负责解析指令的解析完第一条, 紧接着解析第二条, 第三条…
…
- 遇到条件判定, 循环, 函数调用时, 谁是第二条指令? 或 上一条指令还没有解析完成时, 负责执行的已经执行完成, 怎样执行未知的下一条语句 能够将程序运行效率最大化?
CPU有很多相关的运算形式, 如: 分支预测…
1.3.1.3.1 分支预测
通俗来讲, 就是CPU去猜测下一条指令, 如果猜中了, 流水线就继续跑, 如果猜错了, 重新按照实际情况再读一遍指令
即: 第N条指令还没执行完, 就要猜测第N+1条指令是什么, 并为其安排流水线
#问题 重读一遍指的是继续猜吗? : 边执行边猜, 猜不中就等其按照流程执行完就行了
比如: 御魂计算器正在计算你的御魂需求, 然后你同时也乱配御魂, 如果你乱配的刚好满足了你的御魂需求, 就不用继续计算了. 但如果直到算完了也配不出来, 问题不大, 因为已经算完了, 然后就可以接着算下一套御魂…
1.3.1.3.2 时钟周期
流水线之间为了相互配合而确定, 每个指令的执行都需要消耗几个时钟周期
- 1.9GHz CPU: 1s有19亿个时钟周期
CPU, 存储器, 输入设备, 输出设备… 都是"硬件"设备, 普通用户要和硬件打交道, 还是有点难度. 于是诞生了操作系统这个软件, 用于管理各种硬件设备 并给各种软件提供稳定的运行环境
Windows, MAC, 安卓…都是操作系统, 厂商不同, 细节有差别, 但核心工作都是上面两条
2 操作系统
2.1 结构
2.1.1 应用程序
2.1.2 系统调用
如果你想在控制台打印一个 “hello”, 这个过程中你必须调用库函数 printf, 其内部就要调用 操作系统提供的系统调用write, 调用write时就会进入到内核来执行, 内核就要进一步控制硬件, 完成输出过程
2.1.3 操作系统的核心部分: 内核
内核是操作系统实现其主要功能 (管理各种硬件设备, 并给各种软件提供稳定的运行环境) 的部分
当我们使用操作系统进入内核时, 涉及到 用户态 到 内核态 间的切换, 要运行的程序有很多, 但内核只有一个, 就很可能要排队等待
2.1.4 驱动程序
是一个由 软件厂商 提供的 软件程序
直接和硬件设备打交道, 驱动程序相当于在 硬件 和 内核 之间来了一层转换
用软件操作硬件一般都很简单, 因为硬件一般都会提供一些"寄存器"
2.1.5 硬件设备
- 操作系统结构示意图: