# CPU
- CPU(Central Processing Unit,中央处理单元),又称为中央处理器(Central Processor)。是计算机的核心元件,负责运行软件程序,也就是执行程序中的指令。
# 硬件结构
# 主板
- 现代计算机的大部分元件,通常集成在一大块集成电路板上,称为主板。其核心元件为 CPU ,是一小块芯片。
- 主板上,CPU 与其它元件之间通过总线进行数据传输。总线根据用途分为三种:
- 地址总线(Address Bus)
- :用于传输地址信息。
- 只能单向传输,只支持 CPU 发送信号到存储器或 I/O 接口电路。
- 地址总线的宽度决定了 CPU 直接寻址的最大范围。
- 例如 8 位机的地址总线由 16 根线组成,可以并行传输 16 bits 的信号,称为 16 位宽度。因此 CPU 最多能对 2^16 个存储单元进行直接寻址。
- 如果存储单元的数量超过该范围,CPU 就无法访问超过的部分。
- 内存每个存储单元的大小为 1 Byte ,因此 8 位机最多寻址 2^16/1024=64 KB 的内存空间,32 位机最多寻址 2^32=4 GB 的内存空间。
- CPU 可以对内存直接寻址,但不能对磁盘直接寻址。因为磁盘的最小存储单元是 block ,不能精确到 byte 。
- 因此磁盘中任何文件,都需要先从磁盘拷贝到内存,才能被 CPU 读写。
- 例如执行一个程序文件时,操作系统会将它从磁盘拷贝到内存,然后让 CPU 运行。
- 控制总线(Control Bus)
- :用于传输 CPU 对其它部件的控制信号,以及其它部件的应答、请求等信号,支持双向传输。
- 数据总线(Data Bus)
- :用于传输数据,支持双向传输。
- 数据总线的宽度通常是字长的倍数。
- 地址总线(Address Bus)
# 寄存器
寄存器(Register)
- :是 CPU 内置的一些容量很小、读写速度超快的存储器,用于暂存 CPU 当前处理的数据、指令。
- 断电时会丢失数据。
寄存器根据用途分为多种类型:
- 数据寄存器
- :用于暂存一些通用的数据。
- 例如 8086 CPU 有多个 16 位的数据寄存器 AX、BX、CX、DX 。每个寄存器也可分为两个 8 位的寄存器使用,比如将 AX 分为成 AH、AL 。
- 段寄存器
- :用于暂存程序的代码段、数据段、栈等内容。
- 指令指针寄存器(Instruction Pointer)
- :用于暂存 CPU 下一条待执行指令在代码寄存器中的偏移地址。
- 标志位寄存器(Flag)
- :用一组二进制位记录当前指令的执行状态。
- 例:
- CF(Carry Flag ,进位标志):若加减运算时,最高位向前发生了进位或借位,则设置 CF=1 ,否则为 0 。只有两个操作数为无符号数时 CF 标志才有意义。
- SF(Sign Flag ,符号标志):若运算结果的最高位为 1 ,则设置 SF=1 ,表示负数。只有两个操作数为带符号数时 SF 标志才有意义。
- OF(Overflow Flag ,溢出标志):若运算结果发生了溢出,则设置 OF=1 ,表示运算结果错误。只有两个操作数为带符号数时 OF 标志才有意义。
- IF(Interrupt Flag ,中断标志):若设置了 IF=1 ,则允许 CPU 响应来自 CPU 外部的可屏蔽中断的中断请求。
- 数据寄存器
# Cache
:CPU 芯片中的一个高速存储器,用于缓存 CPU 从内存经常读取的部分数据。
CPU 执行某个指令时,如果需要读取某数据,会先尝试到 Cache 中查找该数据。此时分为两种情况:
- Cache Hit
- :在 Cache 中找到了,可以立即读取该数据。
- 内核会根据 LRU 算法来自动清理缓存的数据,提高 CPU 读取数据时的 Hit 命中率。
- Cache Miss
- :在 Cache 中没找到,需要到内存中查找该数据。
- Cache Hit
目前的 CPU Cache 一般采用 SRAM 介质,容量为几 MB 。
- 通常存在多级缓存。例如 L1、L2、L3 三级缓存:
- CPU 先到 L1 Cache 中读取数据,如果 Miss 了再到 L2 Cache 中读取数据,以此类推。
- L1、L2、L3 Cache 的读写速度大概为 500 GB/s 、300 GB/s 、200 GB/s 。
- 多核 CPU 一般各有一个独立的 L1 缓存,然后共享同一个 L2、L3 缓存。
- 例:查看本机的 CPU Cache 容量
[root@CentOS ~]# lscpu | grep cache L1d cache: 32K L1i cache: 32K L2 cache: 1024K L3 cache: 36608K
- 通常存在多级缓存。例如 L1、L2、L3 三级缓存:
# Write Buffer
:CPU 芯片中的一个高速存储器,用于缓冲 CPU 写入内存的所有数据。
- 优点:让 CPU 与内存异步工作,减少等待内存 IO 的耗时。
# 多核心
现代 CPU 的时钟频率通常为 3~4 GHz ,执行指令的速度存在上限。为了更快地执行指令,通常在一个物理 CPU 芯片中包含多个核心(Core)处理器,从而能并发执行多个指令。简称为多核 CPU 。
- Core 的数量通常是偶数。
- 每个 Core 独立工作,分别包含一份运算器、控制器、寄存器、缓存等元件。
- 每个 Core 通常分别包含一份 L1~L2 缓存,共用一份 L3~L4 缓存。
常见架构:
- 对称多处理器(Symmetric Multi-Processor ,SMP)
- :各个 CPU 之间平等,共享内存、IO 设备等资源。
- 同时只能有一个 CPU 通过内存总线访问内存,因此 CPU 为 2~4 核时的利用率最高。增加 CPU 数量时,则 CPU 越来越可能因为等待访问内存而阻塞,导致利用率越来越低。
- 非一致内存访问(Non-Uniform Memory Access ,NUMA)
- :在计算机中划分多个节点(node),每个节点包含多核 CPU 、独立内存。
- 各节点的 CPU 可以并发访问本节点的内存,也可以通过互联模块访问其它节点的内存,但比本地内存的访问速度慢。
- SMP 属于一致内存访问。而 NUMA 大幅提高了 CPU 利用率,但跨节点访问内存时慢。
- 大规模并行处理(Massive Parallel Processing ,MPP)
- :将多个 SMP 服务器通过网络连通,组成一个计算机系统。
- 与 NUMA 相比,MPP 不存在跨节点的内存访问。增加 CPU 时,系统性能会线性提升。
- 对称多处理器(Symmetric Multi-Processor ,SMP)
例:查看本机的 NUMA 节点
[root@CentOS ~]# lscpu | grep NUMA NUMA node(s): 2 NUMA node0 CPU(s): 0-15 NUMA node1 CPU(s): 16-31
- 上例中有 2 个 NUMA 节点,分别包含 16 核 CPU 。
- 如果只有 1 个 NUMA 节点,则属于 SMP 架构。
# 型号
# x86
- :美国 Intel 公司发布的一系列 CPU 型号。
- 指令集属于 CISC 。
- x86 CPU 的对外授权较少,主要由 Intel 公司与 AMD 公司交叉授权,两家公司掌控了设计、生产、销售 CPU 的全部流程。
- 有些技术专利已过期,任何公司都可以使用。
- 1987 年,Intel 公司发布了 8086 型号的 CPU ,被 IBM PC 采用而流行起来。此后的 80186、80286、80386 等型号的 CPU 都沿用这种架构,它们都以 86 结尾,因此称为 x86 架构。
- 8086 CPU 的字长为 16 ,地址总线的宽度为 20 bits ,数据总线的宽度为 16 bits 。
- 80386 CPU 的字长为 32 。
- 2003 年,AMD 公司将 x86 架构的字长扩展为 64 ,命名为 AMD64 ,又称为 x86_64、x64 。
# ARM
- :进阶精简指令集机器(Advanced RISC Machine),指英国 ARM 公司发布的一系列 CPU 型号。
- 字长为 32 ,指令集属于 RISC 。
- 成本低、功耗低、散热低,因此用于手机、平板等小型电子设备比 x86 更有竞争力。
- ARM 公司只负责设计 ARM CPU 架构、指令集,不实际生产 CPU ,而是并出售许可证给其它公司,允许其研发、生产 ARM 芯片。
- 2011 年,ARM 公司发布了 ARMv8-A 架构,字长为 64 ,并且重新实现了 ARM 32 位的指令集。
- ARMv8-A 架构划分了 AArch32、AArch64 两种执行状态,分别用于执行 32 位、64 位的指令。
- 2020 年,Apple 公司发布了一款基于 ARMv8-A 架构的 CPU ,称为 Apple Silicon M1 ,用于此后的 MacBook、iPad 等设备。
# 指令
指令:是让 CPU 进行某一操作的命令代码,由操作码和操作数组成。
- 操作码:表示操作符即该操作的类型,比如数据传送、算术运算、逻辑运算、位运算等。
- 操作数:表示该操作的对象,或者对象的地址。
- 有的指令没有操作数,有的指令有 1 个操作数,有的指令有 2 个操作数。
- 8086 CPU 的指令示例:
MOV AL, 18H ; 将源操作数 18H 存入目的操作数中,这里的目的操作数是一个数据寄存器 AL ADD AL, 01H ; 加法:计算目的操作数 AL 加上源操作数,结果存入目的操作数所指的寄存器中 SUB AL, 01H ; 减法:计算目的操作数 AL 减去源操作数,结果存入目的操作数所指的寄存器中 INC AL ; 增量:使操作数的值加 1 MUL 2H ; 无符号数的乘法:计算 AX 中的值乘以该操作数,结果存入 AX DIV 2H ; 无符号数的除法:计算 AX 中的值除以该操作数,结果存入 AX
现代计算机中,运行一个程序时,需要将该程序编译成二进制代码,载入物理内存,让 CPU 读取并执行。
- 这些二进制代码,实际上是一连串能被本机 CPU 识别的指令,称为指令流。
# 指令集
- 指令集:指某个型号的 CPU 可以识别和执行的所有指令,又称为指令系统。
- 常见的 CPU 指令集架构(Instruction Set Architecture ,ISA):
CISC(Complex Instruction Set Computer ,复杂指令集)
RISC(Reduced Instruction Set Computer ,精简指令集)
- 精简了指令数,每个时钟周期执行一条指令。
- 指令的长度统一。
- 精简了寻址方式。
EPIC(Explicitly Parallel Instruction Computing ,显式并行指令集)
VLIW(Very Long Instruction Word ,超长指令集)
# 性能指标
# CPU usage
Linux 内核安排任务给 CPU 执行时,会精确记录每个任务使用 CPU 的时长,便于监控 CPU 的负载大小、使用率。
例:查看 CPU 的累计使用时长
[root@CentOS ~]# cat /proc/stat cpu 443710619 3208 169665123 4484368433 32182610 0 127452828 0 0 0 # CPU 所有核心的使用时长之和 cpu0 202357958 1462 86101880 2258409823 15562055 0 65470261 0 0 0 # 0 号核心的使用时长 cpu1 241352660 1745 83563242 2225958610 16620554 0 61982566 0 0 0 # 1 号核心的使用时长
- 这里的时间单位为 jiffies 。
- 因为 Linux 内核的定时器,每隔 1 jiffies 时长产生一次中断,监控一次 CPU 。
- 1 jiffies 通常等于 10 ms 。
- 这些数值来自 Linux 内存中的计数器。每次 Linux 重启,这些计数器会清零。
- 这里记录了 10 列数值,表示多种任务使用 CPU 的时长:
user # 简称为 us ,表示用户态进程使用的时长,不包括 nice 时长 nice # 简称为 ni ,表示 nice 谦让值大于 0 的用户态进程,使用的时长 system # 简称为 sy ,表示内核态进程使用的时长 idle # 简称为 id ,表示 CPU 的空闲时长,此时没有执行任何任务 iowait # 简称为 wa ,表示 CPU 等待磁盘读写数据的时长 hardware interrupt # 简称为 irq ,表示硬件中断的时长 software interrupt # 简称为 softirq ,表示软件中断的时长 steal # 本机作为虚拟机时,被偷走的 CPU 可用时长,会被宿主机用于执行其它任务,比如运行其它虚拟机 guest # 本机作为宿主机来运行虚拟机时,虚拟机(称为 guest)中进程使用的 CPU 时长 guest_nice # nice 谦让值大于 0 的 guest 进程,使用的 CPU 时长
- 分析这么多种 CPU 使用时长比较麻烦,通常只关注一个性能指标:CPU 使用率(CPU usage)
- 它表示单位时间内,CPU 忙碌时长所占比例。计算公式为
%CPU = ( 1 - idle时长 / 单位时长 ) × 100%
- 假设某一秒,CPU 某个核心的累计 idle 时长增加了 0.2s ,则说明有 0.8s 处于忙碌状态,CPU 使用率为 80% 。
- 统计 CPU 所有核心的使用率,取平均值,就得到了整个 CPU 芯片的使用率。
- 用户难以每秒监控一次
/proc/stat
,可以每分钟监控一次,计算出每分钟的 CPU 使用率,视作平均每秒的 CPU 使用率。
- 它表示单位时间内,CPU 忙碌时长所占比例。计算公式为
- 上面是从 CPU 的角度,统计 CPU 使用时长。而从进程的角度来看,一个进程可能每秒使用多个 CPU 核心,需要累计这些核心的使用时长,才能知道该进程用了多久 CPU 。
- 假设进程每秒累计使用 1.5s CPU ,则 CPU 使用率为 150% 。
- 因此,统计进程的 CPU 使用率时,可能超过 100% ,最大值等于 CPU 核数。
- 这里的时间单位为 jiffies 。
例:假设在一个宿主机上,运行了两个虚拟机 VM1、VM2 。每秒统计一次 CPU 开销。
- 假设 VM1 中,CPU 有 2 个核心,平均每秒的 idle 时长为 0.2s、0.4s 。
- 可知,这 2 个核心,平均每秒的使用时长为 0.8s、0.6s ,合计 1.4s 。
- 可知,这 2 个核心,平均每秒的 CPU 使用率为 80%、60% 。
- 可知,整个 CPU 芯片的平均使用率为 70% 。
- 假设宿主机总共有 2 核物理 CPU ,给 VM1 分配了 2 核虚拟 CPU ,给 VM2 分配了 1 核虚拟 CPU 。
- 此时,虚拟 CPU 的总核数,超过了物理 CPU 的总核数。这一现象称为超额分配,宿主机不足以让所有虚拟机同时跑满 CPU 。
- 假设 VM1 中,全部进程的 CPU 使用率为 150% ,则宿主机的物理 CPU 经常处于忙碌状态,每秒只能腾出 0.5s 可用时长给 VM2 。对于 VM2 而言,它的 CPU 可用时长被 steal 了。
- 假设 VM1 中,CPU 有 2 个核心,平均每秒的 idle 时长为 0.2s、0.4s 。
# load average
:平均负载,指平均每段时间内活跃的进程数。
活跃进程包括:
- 正在被 CPU 运行的进程(Running)
- 等待被 CPU 运行的进程(Runnable)
- 不可中断的进程(比如 iowait)
这些活跃进程,使用的系统资源可能不同,主要分为几类:
- CPU 密集型(CPU intensive)
- :进程长时间使用 CPU 进行运算。因此平均负载高时,CPU 使用率也高。
- IO 密集型(IO intensive)
- :进程长时间等待磁盘 IO 或网络 IO 。因此平均负载高时,CPU 使用率不一定高。
- 一个进程可能同时属于 CPU 密集型、IO 密集型,导致 CPU 使用率高。也可能不属于这两种,几乎不用资源,长时间 sleep 。
- IO 密集型的进程,通常会导致 CPU 长时间处于 iowait 状态,等待磁盘读写数据。
- 大部分 IO 操作能异步进行,CPU 不必一直保持在 iowait 状态,可以切换到 user、system 状态去执行其它任务。
- 因此,如果 CPU 长时间处于 iowait 状态,则说明磁盘负载大。
- 即使磁盘极其忙碌,100% 时间都在 IO 工作,CPU 也未必 100% 时间处于 iowait 状态。
- 因此,如果 CPU 没有长时间处于 iowait 状态,则不一定说明磁盘负载小。
- CPU 密集型(CPU intensive)
如果只存在 CPU 密集型进程,则理想情况下,主机的平均负载数应该刚好等于 CPU 核数,使得每个 CPU 运行一个活跃进程,且没有 CPU 空闲。
- 例:对于有 2 核 CPU 的主机,
- 若平均负载为 1 ,说明 CPU 使用率为 50% 。
- 若平均负载为 2.6 ,说明 CPU 超载了,有部分进程竞争不到 CPU 。
- 实际上,除了 CPU 密集型进程,主机中经常存在一些 sleep 状态的进程,不会增加 CPU 使用率,但会导致平均负载看起来虚高。
- 例如平均负载为 4 时,可能 CPU 使用率为 0% 。
- 例:对于有 2 核 CPU 的主机,
通常用 uptime 命令查看 CPU 平均负载。例:
[root@CentOS ~]# uptime up 21 days, 41 min, 1 users, load average: 0.52, 0.58, 0.59
- up 21 days, 41 min :表示主机的运行时长。每次重启,会重新计时。
- 1 users :表示已登录的用户数。
- load average: 0.52, 0.58, 0.59 :表示最近 1 分钟、5 分钟、15 分钟的平均负载。
# 执行速度
时钟周期(Clock Cycle)
- :CPU 的振荡器发出时钟脉冲的间隔时长。
- 其倒数称为时钟频率。
- 例:一个 4 GHz 的 CPU ,每秒产生
4*10^9
个时钟脉冲,时钟周期为0.25*10*-9
秒。 - 现代 CPU 的时钟频率通常为 3~4 GHz 。如果继续提升时钟频率,则耗电量、散热难度大幅增加。
指令周期
- :CPU 执行一条指令所需的时长。
- 不同指令的指令周期不同,因此通常是计算平均值。
- 早期的 CPU ,每个时钟周期只能执行一条指令。现代的 CPU ,每个时钟周期可能执行多条指令。
- 将 CPU 的时钟频率,乘以每个时钟周期平均执行的指令数(Instructions Per Cycle ,IPC),就得到每秒平均执行的指令数(Instructions Per Second ,IPS)。
字长(Word Size)
- :又称为位元,指 CPU 的算术逻辑单元每次最多能处理多少位二进制数据。
- 现代 CPU 的字长通常是 32 位、64 位。
# 读写速度
假设用 tar 命令压缩文件,则主要流程如下:
- 从磁盘的源文件中读取数据,然后依次拷贝到内存、CPU Cache、CPU Register 。
- CPU 从 CPU Register 读取源数据,执行压缩算法,计算出压缩后的数据。
- CPU 输出压缩后的数据到 CPU Register ,然后依次拷贝到 CPU Cache、内存、磁盘。 可见,CPU 执行程序的耗时主要受以下因素影响:
- CPU 读写数据的速度
- CPU 执行指令的速度
计算机中存在多种存储设备,读写速度从高到低分别为:CPU Register > CPU Cache / Buffer >> 内存 >> 磁盘
- 时钟频率、访问延迟也是这样的顺序。
- 成本则相反顺序。一般磁盘的 IO 速度最慢,但成本最低,因此相同价格时的容量最大。
- 除了读写速度的差异,外存在断电之后能持久保存数据,而其它存储设备通常不能。
CPU 处理数据的速度,比外存读写数据的速度,快很多倍。让 CPU 同步读写外存时,会浪费时间等待 IO 。因此 CPU 采用异步读写,通过内存、Cache 中转数据。
- 读取文件的流程示例:
- CPU 要求读取一个文件,发送指令给磁盘驱动器。然后 CPU 可以执行其它任务,不必浪费时间等待。
- 磁盘驱动器寻址到文件数据,拷贝到磁盘驱动器的内部缓冲区。然后发送中断通知 CPU ,于是 CPU 发生上下文切换,回来执行当前任务。
- CPU 从磁盘拷贝数据到内存。
- 先拷贝到内存中的 Page Cache 空间,此时只能被内核态进程访问。
- 从 Page Cache 拷贝到进程内存空间,此时才能被用户态进程访问。
- 为了避免 CPU 亲自拷贝数据的耗时,通常在计算机中加入 DMA(Direct Memory Access,直接内存访问)控制器,代替 CPU 接收第 2 步的中断信号,将数据拷贝到 Page Cache ,然后发送中断通知 CPU 。
- CPU 从内存拷贝数据到 CPU Cache ,再拷贝到 CPU Register,供 CPU 直接访问。
- 写入文件的流程相反,先由 CPU 从进程内存空间拷贝数据到 Page Cache ,再由 DMA 拷贝到磁盘。
- 读取文件的流程示例: