# CPU

# NUMA

# 概述

Wiki (opens new window)

非统一内存访问架构(英语:Non-uniform memory access,简称NUMA)是一种为多处理器 (opens new window)的电脑设计的内存架构,内存访问时间取决于内存相对于处理器的位置。在NUMA下,处理器访问它自己的本地内存的速度比非本地内存(内存位于另一个处理器,或者是处理器之间共享的内存)快一些。

非统一内存访问架构的特点是:被共享的内存物理上是分布式的,所有这些内存的集合就是全局地址空间 (opens new window)。所以处理器访问这些内存的时间是不一样的,显然访问本地内存的速度要比访问全局共享内存或远程访问外地内存要快些。另外,NUMA中内存可能是分层的:本地内存,群内共享内存,全局共享内存。

# CPU 拓扑:从 SMP 谈到 NUMA (理论篇)

CPU 拓扑:从 SMP 谈到 NUMA (理论篇) | 长亭的网志空间 (opens new window)

CPU 拓扑:从 SMP 谈到 NUMA (理论篇) | 长亭的网志空间 (opens new window)

几个概念:Node,Socket,Core,Thread

NUMA 技术的主要思想是将 CPU 进行分组,Node 即是分组的抽象,一个 Node 表示一个分组,一个分组可以由多个 CPU 组成。每个 Node 都有自己的本地资源,包括内存、IO 等

每个 Node 之间通过互联模块(QPI)进行通信,Node之间可以互相访问,性能会差些,一般用 distance 这个抽象的概念来表示各个 Node 之间互访资源的开销。

image

Node 逻辑概念,Socket 物理概念,代表一个CPU封装,主板上的吃草

Core 就是socket里独立的一组程序执行单元, 物理核

Thread就是逻辑核,或者称之为超线程,提升CPU的处理能力,将Core划分为多个逻辑核(一般是两个),有独立寄存器和终端逻辑。 多个逻辑核共享Core内的执行单元Cache

image

一个 NUMA Node 可以有一个或者多个 Socket,每个 Socket 也可以有一个(单核)或者多个(多核)Core,一个 Core 如果打开超线程,则会变成两个逻辑核(Logical Processor,简称 Processor)。

Node > Socket > Core > Processor

# CPU拓扑

CPU Topology - 团子的小窝 (opens new window)

#!/bin/bash
function get_nr_processor()
{
    grep '^processor' /proc/cpuinfo | wc -l
}

function get_nr_socket()
{
    grep 'physical id' /proc/cpuinfo | awk -F: '{
            print $2 | "sort -un"}' | wc -l
}

function get_nr_siblings()
{
    grep 'siblings' /proc/cpuinfo | awk -F: '{
            print $2 | "sort -un"}'
}

function get_nr_cores_of_socket()
{
    grep 'cpu cores' /proc/cpuinfo | awk -F: '{
            print $2 | "sort -un"}'
}

echo '===== CPU Topology Table ====='
echo

echo '+--------------+---------+-----------+'
echo '| Processor ID | Core ID | Socket ID |'
echo '+--------------+---------+-----------+'

while read line; do
    if [ -z "$line" ]; then
        printf '| %-12s | %-7s | %-9s |\n' $p_id $c_id $s_id
        echo '+--------------+---------+-----------+'
        continue
    fi

    if echo "$line" | grep -q "^processor"; then
        p_id=`echo "$line" | awk -F: '{print $2}' | tr -d ' '` 
    fi

    if echo "$line" | grep -q "^core id"; then
        c_id=`echo "$line" | awk -F: '{print $2}' | tr -d ' '` 
    fi

    if echo "$line" | grep -q "^physical id"; then
        s_id=`echo "$line" | awk -F: '{print $2}' | tr -d ' '` 
    fi
done < /proc/cpuinfo

echo

awk -F: '{ 
    if ($1 ~ /processor/) {
        gsub(/ /,"",$2);
        p_id=$2;
    } else if ($1 ~ /physical id/){
        gsub(/ /,"",$2);
        s_id=$2;
        arr[s_id]=arr[s_id] " " p_id
    }
} 

END{
    for (i in arr) 
        printf "Socket %s:%s\n", i, arr[i];
}' /proc/cpuinfo

echo
echo '===== CPU Info Summary ====='
echo

nr_processor=`get_nr_processor`
echo "Logical processors: $nr_processor"

nr_socket=`get_nr_socket`
echo "Physical socket: $nr_socket"

nr_siblings=`get_nr_siblings`
echo "Siblings in one socket: $nr_siblings"

nr_cores=`get_nr_cores_of_socket`
echo "Cores in one socket: $nr_cores"

let nr_cores*=nr_socket
echo "Cores in total: $nr_cores"

if [ "$nr_cores" = "$nr_processor" ]; then
    echo "Hyper-Threading: off"
else
    echo "Hyper-Threading: on"
fi

echo
echo '===== END ====='
===== CPU Topology Table =====

+--------------+---------+-----------+
| Processor ID | Core ID | Socket ID |
+--------------+---------+-----------+
| 0            | 0       | 0         |
+--------------+---------+-----------+
| 1            | 0       | 1         |
+--------------+---------+-----------+
| 2            | 1       | 0         |
+--------------+---------+-----------+
| 3            | 1       | 1         |
+--------------+---------+-----------+
| 4            | 2       | 0         |
+--------------+---------+-----------+
| 5            | 2       | 1         |
+--------------+---------+-----------+
| 6            | 3       | 0         |
+--------------+---------+-----------+
| 7            | 3       | 1         |
+--------------+---------+-----------+
| 8            | 4       | 0         |
+--------------+---------+-----------+
| 9            | 4       | 1         |
+--------------+---------+-----------+
| 10           | 8       | 0         |
+--------------+---------+-----------+
| 11           | 8       | 1         |
+--------------+---------+-----------+
| 12           | 9       | 0         |
+--------------+---------+-----------+
| 13           | 9       | 1         |
+--------------+---------+-----------+
| 14           | 10      | 0         |
+--------------+---------+-----------+
| 15           | 10      | 1         |
+--------------+---------+-----------+
| 16           | 11      | 0         |
+--------------+---------+-----------+
| 17           | 11      | 1         |
+--------------+---------+-----------+
| 18           | 12      | 0         |
+--------------+---------+-----------+
| 19           | 12      | 1         |
+--------------+---------+-----------+
| 20           | 0       | 0         |
+--------------+---------+-----------+
| 21           | 0       | 1         |
+--------------+---------+-----------+
| 22           | 1       | 0         |
+--------------+---------+-----------+
| 23           | 1       | 1         |
+--------------+---------+-----------+
| 24           | 2       | 0         |
+--------------+---------+-----------+
| 25           | 2       | 1         |
+--------------+---------+-----------+
| 26           | 3       | 0         |
+--------------+---------+-----------+
| 27           | 3       | 1         |
+--------------+---------+-----------+
| 28           | 4       | 0         |
+--------------+---------+-----------+
| 29           | 4       | 1         |
+--------------+---------+-----------+
| 30           | 8       | 0         |
+--------------+---------+-----------+
| 31           | 8       | 1         |
+--------------+---------+-----------+
| 32           | 9       | 0         |
+--------------+---------+-----------+
| 33           | 9       | 1         |
+--------------+---------+-----------+
| 34           | 10      | 0         |
+--------------+---------+-----------+
| 35           | 10      | 1         |
+--------------+---------+-----------+
| 36           | 11      | 0         |
+--------------+---------+-----------+
| 37           | 11      | 1         |
+--------------+---------+-----------+
| 38           | 12      | 0         |
+--------------+---------+-----------+
| 39           | 12      | 1         |
+--------------+---------+-----------+

Socket 0: 0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
Socket 1: 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39

===== CPU Info Summary =====

Logical processors: 40
Physical socket: 2
Siblings in one socket:  20
Cores in one socket:  10
Cores in total: 20
Hyper-Threading: on

===== END =====

# 上下文切换

  1. [03 - 基础篇:经常说的 CPU 上下文切换是什么意思?(上)](http://index.lovedata.net/03 - 基础篇:经常说的 CPU 上下文切换是什么意思?(上).pdf)

  2. [04 - 基础篇:经常说的 CPU 上下文切换是什么意思?(下)(1)](http://index.lovedata.net/04 - 基础篇:经常说的 CPU 上下文切换是什么意思?(下)(1).pdf)

  3. 深入理解Linux内核进程上下文切换 - 云+社区 - 腾讯云 (opens new window)

    1. 进程上下文是进程执行活动全过程的静态描述。我们把已执行过的进程指令和数据在相关寄存器与堆栈中的内容称为进程上文,把正在执行的指令和数据在寄存器与堆栈中的内容称为进程正文,把待执行的指令和数据在寄存器与堆栈中的内容称为进程下文。

    2. 实际上linux内核中,进程上下文包括进程的虚拟地址空间和硬件上下文

  4. 一文让你明白CPU上下文切换 - 知乎 (opens new window)

    1. 什么是 CPU 上下文

      1. CPU 寄存器和程序计数器就是 CPU 上下文,因为它们都是 CPU 在运行任何任务前,必须的依赖环境。

        • CPU 寄存器是 CPU 内置的容量小、但速度极快的内存
        • 程序计数器则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。
    2. 什么是 CPU 上下文切换

      1. 先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。

    3. CPU 上下文切换的类型

      1. 进程上下文切换
      2. 线程上下文切换
      3. 中断上下文切换
    4. 进程上下文切换

      1. image
      2. 内核空间(Ring 0)具有最高权限,可以直接访问所有资源
      3. 用户空间(Ring 3)只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用陷入到内核中,才能访问这些特权资源。
      4. 进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。
    5. 系统调用

      1. 从用户态到内核态的转变,需要通过系统调用来完成。
      2. 一次系统调用的过程,其实是发生了两次 CPU 上下文切换。(用户态-内核态-用户态)
      3. 进程上下文切换,是指从一个进程切换到另一个进程运行;而系统调用过程中一直是同一个进程在运行。
      4. 系统调用过程通常称为特权模式切换,而不是上下文切换。系统调用属于同进程内的 CPU 上下文切换
    6. 进程上下文切换跟系统调用又有什么区别呢

      1. 进程的上下文切换就比系统调用时多了一步:在保存内核态资源(当前进程的内核状态和 CPU 寄存器)之前,需要先把该进程的用户态资源(虚拟内存、栈等)保存下来;而加载了下一进程的内核态后,还需要刷新进程的虚拟内存和用户栈。
    7. 发生进程上下文切换的场景

      1. 时间片耗尽了
      2. 资源不足
      3. sleep函数
      4. 优先级更高的进程运行
      5. 发生硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序
    8. 线程上下文切换

      1. 线程与进程最大的区别在于:线程是调度的基本单位,而进程则是资源拥有的基本单位
      2. 内核任务调度,就是调度线程 进程只是给线程提供了虚拟内存、全局变量等资源。
      3. 当进程只有一个线程时,可以认为进程就等于线程。 - 当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源 (上下文切换时不需要修改)
      4. 线程也有私有数据 寄存器和栈
    9. 发生线程上下文切换的场景

      1. 线程属于不同进程。资源不共享,所以切换过程就跟进程上下文切换是一样。
      2. 线程属于同一个进程。虚拟内存是共享的,虚拟内存这些资源就保持不动,切换线程的私有数据、寄存器等不共享的数据
    10. 中断上下文切换

      1. 为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件打断其他进程时,就需要将进程当前的状态保存下来,这样在中断结束后,进程仍然可以从原来的状态恢复运行。
      2. 跟进程上下文不同,中断上下文切换并不涉及到进程的用户态 中断上下文,其实只包括内核态中断服务程序执行所必需的状态,包括 CPU 寄存器、内核堆栈、硬件中断参数等。
      3. 中断处理比进程拥有更高的优先级
  5. CPU上下文切换 — yang docs documentation (opens new window)

    1. 什么是CPU上下文

      1. CPU 寄存器,是 CPU 内置的容量小、但速度极快的内存。而程序计数器,则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。它们都是 CPU 在运行任何任务前,必须的依赖环境,因此也被叫做 CPU上下文

    2. CPU 上下文切换

      1. 就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。 而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。