# 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)
      • :用于传输数据,支持双向传输。
      • 数据总线的宽度通常是字长的倍数。

# 寄存器

  • 寄存器(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 中没找到,需要到内存中查找该数据。
  • 目前的 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
      

# 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 时,系统性能会线性提升。
  • 例:查看本机的 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 。
      • 假设进程每秒累计使用 1.5s CPU ,则 CPU 使用率为 150% 。
      • 因此,统计进程的 CPU 使用率时,可能超过 100% ,最大值等于 CPU 核数。
  • 例:假设在一个宿主机上,运行了两个虚拟机 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 了。

# load average

:平均负载,指平均每段时间内活跃的进程数。

  • 活跃进程包括:

    • 正在被 CPU 运行的进程(Running)
    • 等待被 CPU 运行的进程(Runnable)
    • 不可中断的进程(比如 iowait)
  • 这些活跃进程,使用的系统资源可能不同,主要分为几类:

    • CPU 密集型(CPU intensive)
      • :进程长时间使用 CPU 进行运算。因此平均负载高时,CPU 使用率也高。
    • IO 密集型(IO intensive)
      • :进程长时间等待磁盘 IO 或网络 IO 。因此平均负载高时,CPU 使用率不一定高。
      • 一个进程可能同时属于 CPU 密集型、IO 密集型,导致平均负载很高。也可能几乎不用资源,长时间 sleep 。
      • 正常情况下,CPU iowait 会在很短时间内结束。
        • 如果 iowait 长时间较高,可能是磁盘 IO 量太大,或系统发生故障,这会导致某些进程一直处于 D 状态,占用 CPU 。
  • 如果只存在 CPU 密集型进程,则理想情况下,主机的平均负载数应该刚好等于 CPU 核数,使得每个 CPU 运行一个活跃进程,且没有 CPU 空闲。

    • 例:对于有 2 核 CPU 的主机,
      • 若平均负载为 1 ,说明 CPU 使用率为 50% 。
      • 若平均负载为 2.6 ,说明 CPU 超载了,有部分进程竞争不到 CPU 。
    • 实际上,除了 CPU 密集型进程,主机中经常存在一些 sleep 状态的进程,不会增加 CPU 使用率,但会导致平均负载看起来虚高。
      • 例如平均负载为 4 时,可能 CPU 使用率为 0% 。
  • 通常用 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 命令压缩文件,则主要流程如下:

    1. 从磁盘的源文件中读取数据,然后依次拷贝到内存、CPU Cache、CPU Register 。
    2. CPU 从 CPU Register 读取源数据,执行压缩算法,计算出压缩后的数据。
    3. CPU 输出压缩后的数据到 CPU Register ,然后依次拷贝到 CPU Cache、内存、磁盘。 可见,CPU 执行程序的耗时主要受以下因素影响:
    • CPU 读写数据的速度
    • CPU 执行指令的速度
  • 计算机中存在多种存储设备,读写速度从高到低分别为:CPU Register > CPU Cache / Buffer >> 内存 >> 磁盘

    • 时钟频率、访问延迟也是这样的顺序。
    • 成本则相反顺序。一般磁盘的 IO 速度最慢,但成本最低,因此相同价格时的容量最大。
    • 除了读写速度的差异,外存在断电之后能持久保存数据,而其它存储设备通常不能。
  • CPU 处理数据的速度,比外存读写数据的速度,快很多倍。让 CPU 同步读写外存时,会浪费时间等待 IO 。因此 CPU 采用异步读写,通过内存、Cache 中转数据。

    • 读取文件的流程示例:
      1. CPU 要求读取一个文件,发送指令给磁盘驱动器。然后 CPU 可以执行其它任务,不必浪费时间等待。
      2. 磁盘驱动器寻址到文件数据,拷贝到磁盘驱动器的内部缓冲区。然后发送中断通知 CPU ,于是 CPU 发生上下文切换,回来执行当前任务。
      3. CPU 从磁盘拷贝数据到内存。
        • 先拷贝到内存中的 Page Cache 空间,此时只能被内核态进程访问。
        • 从 Page Cache 拷贝到进程内存空间,此时才能被用户态进程访问。
        • 为了避免 CPU 亲自拷贝数据的耗时,通常在计算机中加入 DMA(Direct Memory Access,直接内存访问)控制器,代替 CPU 接收第 2 步的中断信号,将数据拷贝到 Page Cache ,然后发送中断通知 CPU 。
      4. CPU 从内存拷贝数据到 CPU Cache ,再拷贝到 CPU Register,供 CPU 直接访问。
    • 写入文件的流程相反,先由 CPU 从进程内存空间拷贝数据到 Page Cache ,再由 DMA 拷贝到磁盘。