硬件结构
冯诺伊曼模型
- 运算器ALU
- 控制器Control Unit
- 存储器Memory Unit
- 输入输出设备 I/O devices
内存
线性存储,基本单位字节(Byte),每一个字节对应一个内存地址(从0开始编号,最后一个地址是内存总字节数-1)。 1 Byte = 8 bit
CPU
- 控制单元Control Unit
- 逻辑运算单元ALU
- 寄存器Accumulator
32位CPU=32bit=一次可计算4字节
寄存器
- 通用寄存器:存放需要计算的数据
- 程序计数器:存放下一条指令所在的“地址”(指令还在内存中)
- 指令寄存器:用来存放当前正在执行的指令
总线
用于CPU和内存、其他设备之间的通信,总线可分为三种:
- 地址总线,指定CPU将要操作的内存的地址
- 控制总线,用于发射、接收信号,如终端、复位等信号。
- 数据总线,用于读取内存的数据
读写内存数据时:一般先地址总线指定地址,再控制总线决定是读或写命令,再通过数据总线来传输数据。
位宽
线路位宽:地址总线条数
32位宽的地址总线和数据总线最好对应32位cpu。32位最大可操作2^32B(4GB)内存(寻址范围)。
64位CPU可以一次读入64位数字或进行64位数字运算。而32位则需要进位等操作。
只有运算大数字时,64位的计算性能才体现出来。
硬件的位宽一般指CPU的位宽,软件的位宽一般指指令的位宽。
64位操作系统经过兼容可以运行32位的软件。但32位的软件却难以执行64位的指令,因为32位的寄存器存不下64位指令。
CPU的指令周期
- 第一步,CPU 读取「程序计数器」的值,这个值是指令的内存地址,然后 CPU 的「控制单元」操作「地址总线」指定需要访问的内存地址,接着通知内存设备准备数据,数据准备好后通过「数据总线」将指令数据传给 CPU,CPU 收到内存传来的数据后,将这个指令数据存入到「指令寄存器」。
- 第二步,「程序计数器」的值自增,表示指向下一条指令。这个自增的大小,由 CPU 的位宽决定,比如 32 位的 CPU,指令是 4 个字节,需要 4 个内存地址存放,因此「程序计数器」的值会自增 4;
- 第三步,CPU 分析「指令寄存器」中的指令,确定指令的类型和参数,如果是计算类型的指令,就把指令交给「逻辑运算单元」运算;如果是存储类型的指令,则交由「控制单元」执行;
简单总结一下就是,一个程序执行的时候,CPU 会根据程序计数器里的内存地址,从内存里面把需要执行的指令读取到指令寄存器里面执行,然后根据指令长度自增,开始顺序读取下一条指令。
CPU 从程序计数器读取指令、到执行、再到下一条指令,这个过程会不断循环,直到程序执行结束,这个不断循环的过程被称为 CPU 的指令周期。
存储器层次结构
寄存器:最快。32/64位CPU的寄存器大多可以存储4/8个字节。
CPU cache: SRAM 静态随机存储器
内存:DRAM 动态随机存储器
硬盘:SSD 固体硬盘或者 HDD 机械硬盘
CPU Cache
L1 一般分为数据缓存dCache和指令iCache缓存两个
L1、L2是每个CPU核心独有,L3是多个CPU共享
Cache line缓存块是CPU从内存读取数据到Cache的单位
缓存命中率
- 提高数据缓存命中率:按内存布局顺序访问
- 提高指令缓存命中率:与CPU分支预测器有关,C/C++提供有显式的分支预测工具likely和unlikely(用其将if中的条件包裹起来就好)。
- 提高多核缓存命中率:把线程绑定到一个CPU核心上。如Linux的Sched_setaffinity方法
数据读写
写入的方法:写直达和写回(大多情况无需读写内存,更好)
缓存一致性 Cache Coherence
- 写传播
- 事务的串行化(锁的概念)
总线嗅探
写传播的实现方式,不能实现事务串行化,总线带宽压力大(总是发出广播事件)
MESI协议
- Modified 相当于Dirty
- Exclusive
- Shared
- Invalidated
伪共享(读写数据时的问题)
当多个线程(分属多个CPU核心)同时读写同一个Cache Line的不同变量时,会导致CPU Cache失效。
解决办法:
- Linux 的 __cacheline_aligned 放在变量后面可使对齐
- 用不会更改的变量(可以是无意义的)去填充
中断
异步的事件处理机智,可以提高系统的并发处理能力。
中断处理程序,要尽快处理完,减少对正常进程运行调度的影响(其他中断可能会丢失)。
Linux:中断分为两个阶段(为了解决中断程序执行过长和中断丢失问题)