性能瓶颈--CPU(上下文切换)

CPU的性能瓶颈不仅仅是cpu负载。因为Linux的性能问题可能是牵一发而动全身的。
比如一个占用内存较高的java程序,导致问题的根本原因是内存不足,但是反映最直观的可能是cpu使用率很高。因为java开启了大量的线程进行GC操作。进而导致cpu使用率高,平均负载也随之升高。所以问题的关键还是追根溯源。再比如进程在竞争CPU的时候并没有真正的运行,但是为什么还会导致系统平均负载升高,这就是上下文切换导致的了。

CPU上下文切换

Linux 是一个多任务的操作系统,它支持远大于CPU数量的任务同时运行,当然并不是真正的同时运行,是每个任务轮流执行CPU分给他们的时间片,让人感觉是同时在运行。
每一个任务运行前,CPU都需要知道任务从哪里加载,又从哪里运行,也就是说,需要系统事先设置好CPU寄存器。
CPU寄存器包含指令寄存器(IR)和程序计数器(PC)。他们用来暂存指令,数据和地址,程序运行的下一条指令地址,这些都是任务运行时的必要环境。因此也被称作CPU上下文
上下文切换就是把前一个任务的CPU上下文保存起来,然后加载新任务的上下文到这些指令寄存器(IR)和程序寄存器(PC)等寄存器中。这些被保存下来的上下文会存储在操作系统的内核中,等待任务重新调度执行时再次加载进来,这样就能保证任务的原来状态不受影响,让任务看起来是连续运行的。
根据场景不同,CPU的上下文切换又分为进程上下文切换,线程上下文切换以及中断上下文切换。

进程上下文切换

在介绍进程上下文切换前,需要先了解进程执行过程中所涉及到的CPU上下文切换,我们称之为特权模式切换
Linux 按照特权等级,将进程的运行空间分为内核空间和用户空间。对应的是CPU的环0(Ring 0) 和 环3(Ring3)。(环2和环1,Linux没用到)

  • 内核空间(Ring0)具有最高权限,可以访问所有资源。
  • 用户空间(Ring3)只能访问受限资源,想要访问物理设备需要陷入内核态中,在内核空间(Ring3)中,才可以访问特权资源。

那么从用户态到内核态的转变就发生一次特权模式切换,如从磁盘上读取一个文件,就发生了一次内核调用,也就发生一次特权模式切换。CPU需要将寄存器中的用户态的指令位置保存起来,截至执行内核态的代码,CPU寄存器需要更新为内核态的新位置,最后跳转到内核态执行内核调用。之后再恢复之前的用户态。这样的一次系统调用过程实际上发生了两次CPU上下文切换。
那这就是进程上下文切换吗?不是的,进程上下文切换只是说一个进程切换到另一个进程上去。
那特权模式切换和进程上下文切换有什么区别吗?
首先进程的管理是有内核进行管理和调度的。进程的切换只能发生在内核态,所以,进程的上下文切换不仅仅包括了虚拟内存,栈,全局变量等用户空间资源,还包括了内核态堆栈,寄存器等内核空间状态。
特别需要注意的是操作系统会将当前任务的虚拟内存一并保存。而Linux中通过TLB来管理虚拟内存到物理内存的映射关系。TLB用于虚拟地址与实地址之间的交互,提供一个寻找实地址的缓存区,能够有效减少寻找物理地址所消耗时间。当虚拟内存被刷新后,TLB也会被更新。如果没有TLB,则每次取数据都需要两次访问内存,即查页表获得物理地址和取数据。在多核的技术下,这会极大的降低程序的执行效率。因为缓存L3 Cache 是被所有核共享的。当TLB被更新后,缓存中的TLB数据会失效,每个CPU都需要从主存中重新载入,一个进程的上下文切换,同时可能影响其他CPU核心上的进程的执行效率。

TLB 三级缓存.png

当需要进程调度的时候,会需要切换上下文,Linux为每个CPU维护一个就绪队列,将活跃的进程按照优先级和等待CPU的时间排序,然后选择需要CPU的进程(优先级高或者等待时间最长的进程)来运行。
什么时候会发生进程调度?

  • 进程的CPU时间片耗尽,被系统挂起,切换到其他等待CPU的进程运行。
  • 进程所需要的系统资源不足。要等待资源满足后才可以运行。这个时候会被系统挂起。
  • 进程通过sleep函数主动将自己挂起。
  • 当有优先级更高的进程运行时,当前进程会被挂起,由高优先级的进程运行。
  • 硬中断发生时,CPU上的进程会被挂起,转而执行内核的中断服务程序。

线程上下文切换

线程的上下文切换就十分的简单了,线程是调度的基本单位,而进程是资源拥有的基本单位。

  • 当进程只有一个线程时,进程可理解就是线程。
  • 当进程拥有多个线程时,线程会共享虚拟内存和全局变量等资源,这些资源在上下文切换中不需要修改。
  • 线程的上下文切换也需要保存自己的一些数据,比如栈,寄存器。这些在上下文切换时是需要保存的。
    也就是当
    1、两个不同进程的线程上下文切换时,此时的切换构成和进程上下文切换一样。
    2、两个线程处于同一进程时,切换只需要切换栈,寄存器等少部分资源。

中断上下文切换

中断时为了快速响应硬件事件的,跟进程上下文不同,中断上下文不涉及进程的用户态。即便打断的是一个用户态的进程,也不需要保存和恢复这个进程的虚拟内存,全局变量等用户态资源。中断上下文只包括内核态中断服务程序执行必需的状态。CPU寄存器,内核堆栈,硬件中断参数。

查看系统的上下文切换情况

既然过多的上下文切换会把CPU的时间消耗在上下文环境的保存上,并没有充分利用其计算功能。那就需要查看当前系统的上下文切换情况了。

vmstat

查看系统的上下文切换情况

$ vmstat 1
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      0 2453028   2108 1211540    0    0     3    10   27   35  0  0 99  0  0
 0  0      0 2453004   2108 1211572    0    0     0     0   52   66  0  0 100  0  0

  • cs (centext switch) 每秒的上下文切换次数
  • in (interrupt) 每秒的中断次数
  • r (Runing or Runnable) 就绪队列的长度,也就是正在运行和等待CPU的进程数。
  • b (Blocked) 不可中断睡眠状态的进程数
pidstat

pidstat 可以看到具体的某个应用程序的上下文切换情况。

$ pidstat -w 1
Linux 3.10.0-957.el7.x86_64 (localhost.localdomain)     2019年03月23日     _x86_64_    (2 CPU)

13时51分31秒   UID       PID   cswch/s nvcswch/s  Command
13时51分32秒     0         9      9.80      0.00  rcu_sched
13时51分32秒     0        11      0.98      0.00  watchdog/0
13时51分32秒     0        12      0.98      0.00  watchdog/1
13时51分32秒     0       481      0.98      0.00  kworker/1:3
13时51分32秒     0      4683     18.63      0.00  xfsaild/dm-0
13时51分32秒     0      9433      9.80      0.00  vmtoolsd
13时51分32秒     0     27261      0.98      0.00  kworker/u256:0
13时51分32秒     0     40878      2.94      0.00  kworker/0:1
13时51分32秒     0     40880      0.98      0.00  pidstat
  • cswch (voluntary context switches) 自愿上下文切换,指的是进程无法获得所需的资源导致的上下文切换。比如I/O不足,内存不足。
  • nvcswch (non voluntary context switches) 非自愿上下文切换,指的是 进程由于时间片已到等原因,被系统强制调度,进而发生上下文切换。比如大量进程在争抢CPU

模拟上下文切换的场景

这里使用的sysbench,模拟操作系统的多线程调度瓶颈。以10个线程运行5分钟的基准测试。模拟多线程切换

$  sysbench --threads=100 --max-time=300 threads run

观察vmstat的输出

 $ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
17  0      0 2442576   2108 1217720    0    0     3    10   27   80  0  0 99  0  0
11  0      0 2442576   2108 1217720    0    0     0     0 23882 1325686 10 89  1  0  0
10  0      0 2442576   2108 1217720    0    0     0     0 22815 1348568 11 89  0  0  0
17  0      0 2442576   2108 1217720    0    0     0     0 23978 1328481 10 89  1  0  0
11  0      0 2442576   2108 1217720    0    0     0    25 23081 1344558 11 89  0  0  0
14  0      0 2442576   2108 1217720    0    0     0     0 23804 1305193 11 89  1  0  0
19  0      0 2442576   2108 1217720    0    0     0     0 22418 1254798 11 89  0  0  0

可观察到 cs 瞬间上升(第一行是开机以来的参数的平均值)。
接下来观察pidstat ,这里需要加上 -t 参数,显示线程,要不不带参数,看不到sysbench。

$ pidstat -u -w 1
平均时间:   UID       PID    %usr %system  %guest    %CPU   CPU  Command
平均时间:     0     42213    0.00    0.05    0.00    0.05     -  kworker/0:0
平均时间:     0     42218   19.82  100.00    0.00  100.00     -  sysbench
平均时间:     0     42321    0.33    1.00    0.00    1.33     -  pidstat

平均时间:   UID       PID   cswch/s nvcswch/s  Command
平均时间:     0         2      0.05      0.00  kthreadd
平均时间:     0         3      0.29      0.00  ksoftirqd/0
平均时间:     0         9      8.32      0.00  rcu_sched
平均时间:     0        11      0.24      0.00  watchdog/0
平均时间:     0        12      0.24      0.00  watchdog/1
平均时间:     0        14      0.90      0.00  ksoftirqd/1
平均时间:     0        37      0.10      0.00  khugepaged

加上-t后
可以看到 sysbench 发生了大量的自愿上下文切换

$ pidstat -wut 1
平均时间:     0     27261         -      1.85      0.00  kworker/u256:0
平均时间:     0         -     27261      1.85      0.00  |__kworker/u256:0
平均时间:   997         -     27300      1.85      0.00  |__grafana-server
平均时间:   997         -     27304      0.93      0.00  |__grafana-server
平均时间:   997         -     27307      1.85      0.00  |__grafana-server
平均时间:   997         -     27341      1.85      0.00  |__grafana-server
平均时间:     0         -     42219   2879.63  11057.41  |__sysbench
平均时间:     0         -     42220   1902.78  13955.56  |__sysbench
平均时间:     0         -     42221   4470.37  11027.78  |__sysbench
平均时间:     0         -     42222   1792.59  17249.07  |__sysbench
平均时间:     0         -     42223   2542.59   7395.37  |__sysbench
平均时间:     0         -     42224   1183.33  16775.93  |__sysbench
平均时间:     0         -     42225   3678.70  12963.89  |__sysbench
平均时间:     0         -     42226   3208.33  15801.85  |__sysbench
平均时间:     0         -     42227   2602.78  13896.30  |__sysbench

通过dstat命令可以看到,除了有很多的资源上下文切换,还有很多中断。

$ dstat
You did not select any stats, using -cdngy by default.
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
  1   2  97   0   0   0|5683B   19k|   0     0 |   0     0 | 583    30k
  8  85   7   0   0   0|   0     0 | 132B 1060B|   0     0 |  28k  977k
  8  82  10   0   0   0|   0     0 | 132B  620B|   0     0 |  25k 1025k

这是重调度中断(RES),在唤醒空闲状态的CPU来重新调度新任务运行。具体可看 /proc/interrupts。

小结

一般上下文切换在数百到一万之内上下文切换超过1万,很可能遇到性能问题。需要具体看看了。

  • 资源上下文切换时说明进程在等待资源,有可能发生了I/O等问题;
  • 非自愿上下文切换,说明进程在被强制调度,也就是在争抢CPU;
  • 中断次数多了,说明CPU在被中断处理程序占用。可以通过/proc/interrupts 查看。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,425评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,058评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,186评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,848评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,249评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,554评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,830评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,536评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,239评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,505评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,004评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,346评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,999评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,060评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,821评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,574评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,480评论 2 267

推荐阅读更多精彩内容