校招面试问到Linux CPU不用怕,来看看这份宝典
原创小目网易游戏运维平台
小目
网易游戏资深运维工程师,负责《阴阳师》、《明日之后》等多款游戏运维工作。
又是一年校招季,在面试的过程中发现很多同学对 Linux CPU 使用的细节和原理不是很了解,所以这篇文章整理了一些面试过程中 Linux CPU 调度可能会问到的问题,希望对各位同学有所帮助。
从 top 命令开始
通常情况下同学们都会回答用top
命令来查看 CPU 使用率,那么第一问就来了:
-
top
命令下的那些输出分别代表什么呢? -
那些数字又是怎么算出来的呢?
CPU 使用率是怎么计算的
CPU 使用率计算的原理
先来说第二个问题,CPU 的使用率是如何算出来的。在 top
命令中看到的 cpu 使用都是一个百分比,主要包括这两个:
-
所有 CPU 核使用率聚合后的百分比
-
每个进程 CPU 使用率百分比
CPU 运行的时间被分为一个个的时间片(time slices),又称为 ticks。一个 tick 是系统能识别的最小的时间间隔了,根据 CPU 时钟频率的不同,每秒钟有多少个 ticks 也不同。
从 /proc/stat 获取从机器启动开始至今花了多少 ticks (不同架构的机器输出会有不同)
# cat /proc/stat
cpu 8772231776 3 2071565293 180450735957 98308067 485278 400223124 0 0 0
cpu0 378973514 0 97049915 5442776752 21309455 2138 20192322 0 0 0
cpu1 385703831 0 96690059 5459587181 7568642 2528 13523191 0 0 0
cpu2 385786642 0 97211447 5465451030 3966208 2773 12694622 0 0 0
cpu3 395145419 0 97576302 5457525455 2946605 2848 12569205 0 0 0
cpu4 386867254 0 96869713 5467574789 2607170 2788 12157640 0 0 0
cpu5 385569388 0 96653777 5469143458 2382857 2954 12127683 0 0 0
上面每列的数字分别表示花在下列事情上的 time slices 数:
-
user
-
nice
-
system
-
idle
-
iowait
-
irq
-
softirq
-
steal
-
guest
-
guest_nice
其中每个分类我们会在下面再解释
计算方式
CPU 总时间 = user + nice + system + idle + iowait + irq + softirq + steal
CPU idle 时间 = idle + iowait
CPU usage 时间 = CPU 总时间 - CPU idle 时间
CPU 使用率 = CPU useage 时间 / CPU 总时间
计算每个进程 CPU 使用率时,分母是单个 core 的总时间,所以当进程使用多个 core 的时候,CPU 使用率可能会超过 100%
不同种类的 CPU 使用
上面提到了很多不同的 CPU 使用,他们的含义在 manpage 里就可以看到,在网上也可以搜到很多
user: Time spent in user mode.
nice: Time spent in user mode with low priority (nice).
system: Time spent in system mode.
idle: Time spent in the idle task. This value should be USER_HZ times the second entry in the /proc/uptime pseudo-file.
iowait: Time waiting for I/O to complete.
irq: Time servicing interrupts.
softirq: Time servicing softirqs.
steal: Stolen time, which is the time spent in other operating systems when running in a virtualized environment
guest: Time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.
guest_nice: Time spent running a niced guest (virtual CPU for guest operating systems under the control of the Linux kernel).
但是我们想考察的并不是上述概念的背诵,而是想让同学们举例说出机器处于什么样的状态会导致上述的某个或某几个的数值上涨,这个问题就留给同学们自己去思考。
CPU 性能相关概念
下面介绍一些可能会问到的 CPU 性能相关的概念以及原理,希望同学们能以这些内容为基础继续探索 Linux CPU 的知识。
Run Queue
用来存放等待被执行进程的队列,这个队列中的进程可能是 runnable 或 uninterruptible。每个 core 都有各自的 run queue 来存放符合条件的进程。
-
runnable 的进程可以认为是所有的资源全部准备就绪,就等 CPU 来执行了
-
uninterruptible 的进程属于不可中断睡眠进程,可以任务它正在等待 IO 的返回,比如说在等待磁盘读操作返回的一个进程,就处于 uninterruptible 状态(D 状态)
vmstat 可以看到有多少在 run queue 中的进程是 runnable 或 blocked
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 565716 80270304 415776 3862448 0 0 0 4 0 0 5 1 94 0 0
2 0 565716 80270416 415776 3862448 0 0 0 20 22931 97294 5 1 94 0 0
5 0 565716 80269632 415776 3862468 0 0 0 80 30753 79666 4 1 95 0 0
loadaverage
关于 loadaverage 的内容可以参考这篇文章:面试被问到 Linux loadavg,看这篇推文就不怕被问倒了
上下文切换(context switching)
什么是上下文切换
一个 core 要并发处理多个进程,只有交替进行,交替进行的过程:
-
保存当前执行进程的所有上下文信息,为了下次切回来执行这个进程
-
获取新进程的上下文信息,执行新进程
上下文切换的优缺点
-
可以让一个 core 并发处理多个进程
-
大量的上下文切换会导致性能问题
-
每次处理器进行切换的时候,必须把当前进程的上下文放到内存
-
同时从内存获取新进程的上下文
-
上面的过程需要时间和计算力
-
什么时候发生上下文切换
-
处理器主动进行切换,通过内核调度
-
软/硬中断导致上下文切换
多核心的情况下的性能问题
多核心的情况下,进程运行可能是什么样的
-
进程在执行的过程中可能会在不同 core 之间漂移,一会在这个 core 上执行,一会在另一个 core 上执行
-
进程在哪个 core 上执行取决于设置的亲和度(affinity)
-
通常进程使用所有的 core (雨露均沾)
-
当进程在这个 core 上运行切换到另一个 core 上运行的时候,需要清除缓存(flush cache)
-
频繁地 flush cache 会导致性能问题
怎么查看并设置进程的 CPU 亲和度
可以用 taskset
查看并设置进程的 CPU 亲和度
:~$ taskset -c -p 18232
pid 18232's current affinity list: 0-31
:~# taskset -p f 18232
pid 18232's current affinity mask: ffffffff
pid 18232's new affinity mask: f
:~# taskset -pc 18232
pid 18232's current affinity list: 0-3
再深入一点查看 CPU 的使用
这里主要是考察原理和实践的结合,大部分时候只有社招才会问这些问题,不过也提供给同学们一个简单的索引帮助以后面的学习。
CPU 总的使用量及 loadaverage 的升高,可以给你一个大体的感觉,系统负载在变高,然而你很难去描述具体是为什么会变高。
比如是哪个进程让 CPU 使用率升高的?loadaverage 升高了是哪个资源(CPU、磁盘 IO 等)的使用量高了?
所以这个时候你会用哪些命令看哪些指标去分析问题的 root cause
一些常用的命令
每个核的 CPU 使用率
-
mpstat -P ALL 1
-
top
每个进程的 CPU 使用率
-
top
CPU run queue 大小
-
vmstat 看 r 列
CPU/ 每个线程的 run queue 延迟
-
/proc/schedstat
-
/proc/PID/sched
-
/proc/PID/schedstat
-
perf sched
perf sched 怎么用
perf sched 是 perf 命令的子命令,主要用来分析内核 CPU 调度器的行为。这里用两个例子简单介绍一下这个命令的用法。
dump 调度器事件(scheduler events)到 perf.data 文件来分析
[root@stretch:~]# perf sched record -- sleep 1
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.190 MB perf.data (146 samples) ]
查看每个线程的 run queue 延迟
perf sched latency
-----------------------------------------------------------------------------------------------------------------
Task | Runtime ms | Switches | Average delay ms | Maximum delay ms | Maximum delay at |
-----------------------------------------------------------------------------------------------------------------
watchdog/0:11 | 0.000 ms | 1 | avg: 0.051 ms | max: 0.051 ms | max at: 413269.258775 s
watchdog/1:14 | 0.000 ms | 1 | avg: 0.047 ms | max: 0.047 ms | max at: 413269.258771 s
haproxy:464 | 0.051 ms | 1 | avg: 0.034 ms | max: 0.034 ms | max at: 413268.940086 s
mcollectived:511 | 0.545 ms | 10 | avg: 0.031 ms | max: 0.034 ms | max at: 413269.282287 s
ntpd:1018 | 0.071 ms | 1 | avg: 0.028 ms | max: 0.028 ms | max at: 413269.575476 s
kworker/0:0:25140 | 0.279 ms | 9 | avg: 0.025 ms | max: 0.029 ms | max at: 413268.759815 s
sleep:30072 | 1.894 ms | 2 | avg: 0.024 ms | max: 0.033 ms | max at: 413269.691943 s
kworker/1:1:25107 | 0.927 ms | 3 | avg: 0.024 ms | max: 0.030 ms | max at: 413269.431884 s
rcu_sched:7 | 0.112 ms | 5 | avg: 0.013 ms | max: 0.024 ms | max at: 413268.718723 s
perf_4.9:30070 | 3.314 ms | 1 | avg: 0.008 ms | max: 0.008 ms | max at: 413269.692124 s
migration/1:15 | 0.000 ms | 1 | avg: 0.005 ms | max: 0.005 ms | max at: 413268.690119 s
-----------------------------------------------------------------------------------------------------------------
TOTAL: | 7.194 ms | 35 |
---------------------------------------------------
-
可以看到 CPU 执行的时间,平均延迟,最大延迟,上下文切换次数
查看 CPU 上下文切换事件
# perf sched map
*A0 993552.887633 secs A0 => perf:26596
*. A0 993552.887781 secs . => swapper:0
. *B0 993552.887843 secs B0 => migration/5:39
. *. 993552.887858 secs
. . *A0 993552.887861 secs
. *C0 A0 993552.887903 secs C0 => bash:26622
. *. A0 993552.888020 secs
. *D0 . A0 993552.888074 secs D0 => rcu_sched:7
. *. . A0 993552.888082 secs
. . *C0 A0 993552.888143 secs
. *. . C0 A0 993552.888173 secs
. . . *B0 A0 993552.888439 secs
. . . *. A0 993552.888454 secs
. *C0 . . A0 993552.888457 secs
. C0 . . *. 993552.889257 secs
. *. . . . 993552.889764 secs
. . *E0 . . 993552.889767 secs E0 => bash:7902
...]
-
A0, B0…:这些代表不同进程,在 => 右边看映射关系,只为了显示简洁
-
有多少个 CPU 核,在时间数字的左边就会有多少列,表示每个 CPU 核的情况
-
* 代表发生了上下文切换,比如第一行 *A0 代表第六个 core 进行上下文切换开始跑 A0 进程
-
空白表示已经开始在那个 core 上 trace 一个 event,很快就有显示了
-
. 表示 CPU 核空闲
关于 CPU 使用率的误解
最后说一些扩展的内容,来源是 Netflix 性能工程师 Brendan Gregg(https://en.wikipedia.org/wiki/Brendan_Gregg) 的文章 CPU Utilization is Wrong(http://www.brendangregg.com/blog/2017-05-09/cpu-utilization-is-wrong.html)
当你看到 CPU 使用 90% 的时候,你以为是这样的
事实上可能是这样的
其中 stalled 表示 CPU 没有在执行指令(instruction),而是在等内存 IO
上面这个图的比例是 Brendan Gregg 在生产环境中预估的,也就是 CPU 可能大部分时间都不在执行指令,而是在等内存 IO
为什么 CPU 会 stalled
CPU 比内存的速度快很多,而且随着技术的发展,这个差距越来越大,导致 CPU 指令执行的时间会远小于内存 IO 的时间,所以导致了 CPU stalled。
对于 CPU stalled 有哪些指标可以评估
看整个系统的 CPU stalled
perf stat -a --sleep 10
Performance counter stats for 'system wide':
641398.723351 task-clock (msec) # 64.116 CPUs utilized (100.00%)
379,651 context-switches # 0.592 K/sec (100.00%)
51,546 cpu-migrations # 0.080 K/sec (100.00%)
13,423,039 page-faults # 0.021 M/sec
1,433,972,173,374 cycles # 2.236 GHz (75.02%)
<not supported> stalled-cycles-frontend
<not supported> stalled-cycles-backend
1,118,336,816,068 instructions # 0.78 insns per cycle (75.01%)
249,644,142,804 branches # 389.218 M/sec (75.01%)
7,791,449,769 branch-misses # 3.12% of all branches (75.01%)
10.003794539 seconds time elapsed
-
关键看 instruction per cycle,表示每个 CPU 时钟周期平均可以运行多少条指令
-
简单的看是越高越好(CPU 使用更充分)
-
对于 4-wide 的 CPU,一个 cycle 满载可以执行 4 条指令,所以执行 0.78 条指令差不多使用了 19.5% 的 CPU。新的 intel CPU 已经是 5-wide 的了。
看每个进程的 IPC(Instruction Per Cycle)
tiptop
对于 IPC 该怎么看
以 1.0 为界(具体根据实际来判断):
-
IPC > 1.0 表示 CPU 执行密集,优化方向:
-
软件:代码里减少不必要的操作、缓存等
-
硬件:用时钟频率更高的 CPU,加核,超线程
-
-
IPC < 1.0 表示内存 IP 密集,优化方向:
-
软件:代码更加缓存友好
-
硬件:用缓存更大的 CPU,更快的内存
-
结语
Linux 的 CPU 调度是一个很复杂系统,这篇文章只能作为一个引子,希望同学们可以得到启发进行更深入的研究,期待可以和各位在网易游戏相遇。
往期精彩
﹀
﹀
﹀
面试被问到 Linux loadavg,看这篇推文就不怕被问倒了
MySQL 8.0 New Feature:Clone 插件
MySQL 8.0 New Feature:NOWAIT and SKIP LOCK