mysql 上下文切换高_系统性能分析之CPU上下文切换

多任务的实施依赖于CPU上下文的切换,其中就要理解CPU寄存器(CPU Register)和程序计数器(Program Counter)。前者是CPU内置的容量小、速度极快的内存;程序计数器保存正在执行的指令位置,或即将执行的下一条指令的位置,它们都是CPU运行任何任务前,必须的依赖环境,因此被叫做CPU上下文切换。过程大致如下:

首先,保存前一任务的CPU上下文(CPU寄存器和程序计数器)

然后,加载更新新任务的CPU上下文到CPU寄存器和程序计数器

最后,跳到程序计数器所指的新位置,运行新的任务

根据任务不同,可以分为进程上下文切换、线程上下文切换、中断上下文切换

进程上下文切换,是指从一个进程切换到另一个进程,进程由内核管理和调度,进程切换只能发生在内核态。进程在用户态运行称为进程的用户态,陷入内核运行时,称为进程的内核态。但是从用户态转变为内核态,需要系统调用(system call ).一次系统调用发生两次CPU上下文的切换:

CPU寄存器和程序计数器先保存用户态的CPU上下文,接着为了执行内核态代码,更新加载内核态指令的位置,之后跳到内核态执行内核任务;

系统调用结束后,CPU寄存器需要恢复到原来保存的用户态,之后切换到用户空间,继续执行进程。

注意的是,系统调用不涉及到虚拟内存等用户态的资源,也不切换进程。

触发进程上下文切换的场景:

(a)CPU时间分为一段段的时间片,公平分配给各进程,当某进程的时间片耗尽,就会被系统挂起,切换到另一进程

(b)进程在资源不足时(内存不足),要等到资源满足时才能运行,这时进程也会被挂起,并由系统调度其他进程运行

(c)当进程遇到睡眠函数sleep这样的方法时,会主动挂起,切换到其他进程

(d)当遇到高优先级进程时,为保证高优先级的进程运行,当前进程会被挂起,由高优先级进程运行

(e)发生硬件中断时,CPU上的进程会被中断挂起,转而执行内核中的中断服务程序

线程上下文切换

线程是任务调度的最小单位,进程是资源分配的最小单位,实质是:内核中的任务调度,实际上的调度对象是线程;进程则是为线程提供虚拟内存、全局变量等资源。也可以这样理解进程和线程:

(1)当进程只有一个线程时,可以认为进程就等于线程

(2)当进程有多个线程时,这些线程共享相同的虚拟内存、全局变量等资源,这些资源在上下文切换时不需要修改

(3)线程也有自己私有数据,如栈和寄存器等,这些私有数据在上下文切换时是需要保存的

线程上下文切换分为两种情况:(1)前后两个线程属于不同的进程,由于资源不共享,此时的线程切换就和进程切换一样

(2)前后两个线程属于相同的进程,由于虚拟内存等资源共享,切换时,只切换线程的私有数据等不共享数据。

可以看到同进程的不同线程切换消耗的资源较小,效率高

中断上下文切换

为了快速响应硬件事件,中断处理会打断进程的正常调度和执行,转而调用中断服务程序,响应设备事件。

和进程上下文切换不同,中断上下文切换不涉及进程的用户态,它只包括内核态中断服务程序执行所必需的状态,包括CPU寄存器、内核堆栈、硬件中断参数等。

对同一CPU来说,中断处理比进程拥有更高的优先级;和进程切换一样,中断切换也消耗CPU,切换次数过多也消耗大量的CPU,甚至会影响系统的整体性能。

小结

不管哪种场景导致的上下文切换,我们都应该知道:

(1)CPU上下文切换,是保证Linux系统多任务处理的核心功能之一,一般情况下不用我们关注

(2)过多的上下文切换,会把CPU时间消耗在寄存器、虚拟内存等数据的保存和恢复上,缩短了进程真正运行时间,导致系统整体性能下降。

案例分析:

vmstat主要用来分析系统的内存情况,也常用来分析CPU上下文切换和中断的次数

root@andy:~# vmstat 1 1

procs -----------memory----------------------------- ---swap-- -----------io-------- -system-- ------cpu-----

r  b    swpd   free     buff  cache    si   so    bi    bo   in   cs   us  sy  id   wa  st

0  0      0   2437540  97108 956224   0    0   161    72   54   58  1   2  92   5   0

注释

Procs

r: The number of runnable processes (running or waiting for run time).

b: The number of processes in uninterruptible sleep.

IO

bi: Blocks received from a block device (blocks/s).

bo: Blocks sent to a block device (blocks/s).

System

in: The number of interrupts per second, including the clock.

cs: The number of context switches per second.

从上面的例子可以看到系统上下文切换为58次,系统中断为54次;运行或等待运行的进程r为0,不可中断的睡眠进程b为0

vmstat只能看到系统总体上下文切换情况,查看各进程的详细情况,需要前面使用的pidstat命令-w就能详细查看

root@andy:~# pidstat  -w

Linux 4.18.0-12-generic (andy)  05/20/20        _x86_64_        (4 CPU)

14:32:52      UID       PID   cswch/s  nvcswch/s  Command

14:32:52        0         1      5.15      5.67  systemd

14:32:52        0         2      0.64      0.00  kthreadd

14:32:52        0         3      0.00      0.00  rcu_gp

14:32:52        0         9      1.37      0.02  ksoftirqd/0

14:32:52        0        10     33.00      0.03  rcu_sched

....................................

-w     Report  task  switching activity (kernels 2.6.23 and later only)

cswch/s

Total number of voluntary context switches the task made per second.

A voluntarycontext switch occurs whenatask blocks because it requires a resource that isunavailable.

nvcswch/s

Total number of non voluntary context switches the task made per second.  A involuntary  context  switch  takes place when a task executes for the duration of its timeslice and then is forced to relinquish the processor.

输出结果中重点关注cswch/s和nvcswch/s的次数,因为它们意味着不同的性能问题。

自愿上下文切换(cswch):当进程无法获得所需要的资源时,就会导致上下文切换,比如I/O、内存资源不足。

非自愿上下文切换(nvcswch):进程由于时间片已到等原因,被强制调度,发生上下文切换,比如大量进程争夺CPU。

在进行案例模拟前,首先看下空闲系统的上下文切换

root@andy:~# vmstat  1 3

procs -----------memory---------------------------- ---swap-- ----------io--------- -system-- ------cpu-----

r  bswpdfreebuff  cachesi   so    bi    boin  csussy idwa st

1  0      0 2484140  95048 919748    0    0    45    1117200  0 98  1  0

0  0      0 2484148  95048 919748    0    0     0     027230  0 100  0  0

0  0      0 2484148  95048 919748    0    0     0     029300  0 100  0  0

机器提前准备安装好sysbench和sysstat

在第一个终端里,执行sysbench模拟系统多线程调度的瓶颈

root@andy:~# sysbench --threads=5 --max-time=60 threads run

以5个线程运行60秒为基准测试,模拟多线程切换的问题

第二个终端,执行vmstat观察上下文切换

root@andy:~# vmstat  1

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----

r  b 交换 空闲 缓冲 缓存si   so    bi    bo   in   cs us sy id wa st

1  0    780 218488 112644 981264    0    0   111    50   41   49  1  1 95  3  0

0  0    780 218448 112644 981264    0    0     0     0   71   94  0  0 100  0  0

0  0    780 218448 112644 981264    0    0     0     0   64   90  0  0 100  0  0

5  0    780 215968 112644 982552    0    0  1224     0 2649 129107  7 19 70  4  0

5  0    780 215960 112644 982488    0    0     0     0 10481 367900 32 61  7  0  0

5  0    780 215960 112644 982488    0    0     0     0 11893 347654 35 58  6  0  0

5  0    780 215960 112644 982488    0    0     0     0 10051 335603 30 63  7  0  0

能明显看到cs切换飙升到三十多万,中断in飙升到一万多,这明显影响系统性能;r为5,系统逻辑CPU为4,存在使用CPU的竞争,在CPU中us与sy相加为93左右,且sy在六十上下,说明内核占用CPU较多。

综合可以得出:运行或等待运行的系统就绪队列(进程)过多,导致上下文切换的飙升,上下文的切换又导致CPU的使用率上升。

第三个终端,用pidstat查看CPU和进程上下文切换的情况

root@andy:~# pidstat -w -u  1 #-w表示进程切换指标-u表示CPU使用指标1表示每个1秒输出1次(需Ctrl+c结束)

Linux 4.18.0-12-generic (andy)  2020年05月20日_x86_64_        (4 CPU)

22时04分20秒UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command

22时04分21秒0      3142    0.00    1.00    0.00    0.00    1.00     0  sshd

22时04分21秒0      4181  100.00  100.00    0.00    0.00100.003sysbench

22时04分21秒0      4188    1.00    2.00    0.00    0.00    3.00     0  pidstat

22时04分20秒UID       PID   cswch/s nvcswch/s  Command

22时04分21秒0         9      2.00      0.00  ksoftirqd/0

22时04分21秒0        1020.000.00rcu_sched

22时04分21秒0        30      2.00      0.00  ksoftirqd/3

22时04分21秒0        37      1.00      0.00  kworker/0:1-mpt_poll_0

22时04分21秒0      309952.000.00kworker/u256:0-events_freezable_power_

22时04分21秒0      314247.000.00sshd

22时04分21秒0      412848.000.00kworker/u256:2-events_unbound

22时04分21秒0      4180      1.00      0.00  vmstat

22时04分21秒0      4188      1.0037.00pidstat

.............

可以看出CPU使用率升高是sysbench导致的,但上下文切换是由其他进程导致的,比如自愿上下文切换的rcu_sched、kworker等,非自愿上下文切换的pidstat.

我们看到这些切换总共加起来也就两百左右,怎么cs能飙到三十多万,中断飙到一万多呢?使用pidstat -wt 1显示线程指标,如下:

root@andy:~# pidstat -wt  1  #-wt参数输出线程的上下文切换指标

Linux 4.18.0-12-generic (andy)  2020年05月20日_x86_64_        (4 CPU)

22时51分58秒UID      TGID       TID   cswch/s nvcswch/s  Command

22时51分59秒0         9         -      8.00      0.00  ksoftirqd/0

22时51分59秒0         -         9      8.00      0.00  |__ksoftirqd/0

22时51分59秒0        10         -     20.00      0.00  rcu_sched

22时51分59秒0         -        10     20.00      0.00  |__rcu_sched

22时51分59秒0        18         -      1.00      0.00  ksoftirqd/1

22时51分59秒0         -        18      1.00      0.00  |__ksoftirqd/1

22时51分59秒0        37         -      2.00      0.00  kworker/0:1-events

22时51分59秒0         -        37      2.00      0.00  |__kworker/0:1-events

22时51分59秒0        39         -      1.00      0.00  kworker/1:1-mm_percpu_wq

22时51分59秒0         -        39      1.00      0.00  |__kworker/1:1-mm_percpu_wq

22时51分59秒0        55         -      1.00      0.00  kworker/2:1-mm_percpu_wq

22时51分59秒0         -        55      1.00      0.00  |__kworker/2:1-mm_percpu_wq

22时51分59秒0         -      1298      1.00      0.00  |__gmain

22时51分59秒125         -      1606      2.00      0.00  |__mysqld

22时51分59秒125         -      1607      2.00      0.00  |__mysqld

22时51分59秒125         -      1608      2.00      0.00  |__mysqld

22时51分59秒125         -      1609      2.00      0.00  |__mysqld

22时51分59秒125         -      1610      2.00      0.00  |__mysqld

22时51分59秒125         -      1611      2.00      0.00  |__mysqld

22时51分59秒125         -      1612      2.00      0.00  |__mysqld

22时51分59秒125         -      1613      2.00      0.00  |__mysqld

22时51分59秒125         -      1614      2.00      0.00  |__mysqld

22时51分59秒125         -      1615      2.00      0.00  |__mysqld

22时51分59秒125         -      1616      1.00      0.00  |__mysqld

22时51分59秒125         -      1848      1.00      0.00  |__mysqld

22时51分59秒125         -      1849      1.00      0.00  |__mysqld

22时51分59秒125         -      1851      1.00      0.00  |__mysqld

22时51分59秒110         -      2100      1.00      0.00  |__rtkit-daemon

22时51分59秒110         -      2101      1.00      0.00  |__rtkit-daemon

22时51分59秒0      3142         -     18.00      3.00  sshd

22时51分59秒0         -      3142     18.00      3.00  |__sshd

22时51分59秒0      3162         -      2.00      0.00  kworker/3:1-mm_percpu_wq

22时51分59秒0         -      3162      2.00      0.00  |__kworker/3:1-mm_percpu_wq

22时51分59秒0      4196         -     16.00      0.00  kworker/u256:2-events_unbound

22时51分59秒0         -      4196     16.00      0.00  |__kworker/u256:2-events_unbound

22时51分59秒0      4259         -     21.00      0.00  kworker/u256:1-events_unbound

22时51分59秒0         -      4259     21.00      0.00  |__kworker/u256:1-events_unbound

22时51分59秒0         -      4269   9886.00  41982.00  |__sysbench

22时51分59秒0         -      4270  10853.00  85926.00  |__sysbench

22时51分59秒0         -      4271   9830.00  41249.00  |__sysbench

22时51分59秒0         -      4272   9130.00  48229.00  |__sysbench

22时51分59秒0         -      4273   9306.00  58599.00  |__sysbench

22时51分59秒0      4274         -      1.00      9.00  pidstat

22时51分59秒0         -      4274      1.00      9.00  |__pidstat

.............

可以看到虽然sysbench进程(也就是主线程)的上下文切换不多,但是它的子线程切换较多,结论就是上下文切换的罪魁祸首就是sysbench的子线程。

中断的类型可以通过cat /proc/interrupts查看,如下:root@andy:~# watch -d cat /proc/interrupts

Every 2.0s: cat /proc/interrupts                                                         andy: Wed May 20 23:08:42 2020

CPU0       CPU1       CPU2       CPU3

0:         17          0          0          0   IO-APIC    2-edge      timer

1:          0          0          0          9   IO-APIC    1-edge      i8042

8:          1          0          0          0   IO-APIC    8-edge      rtc0

9:          0          0          0          0   IO-APIC    9-fasteoi   acpi

12:        125          0         16          0   IO-APIC   12-edge      i8042

14:          0          0          0          0   IO-APIC   14-edge      ata_piix

15:          0          0          0          0   IO-APIC   15-edge      ata_piix

16:         46          0          0      17031   IO-APIC   16-fasteoi   vmwgfx, snd_ens1371, ens38

17:       5202      37960          0          0   IO-APIC   17-fasteoi   ehci_hcd:usb1, ioc0, ens39

18:          0        217          0          0   IO-APIC   18-fasteoi   uhci_hcd:usb2

24:          0          0          0          0   PCI-MSI 344064-edge      PCIe PME, pciehp

25:          0          0          0          0   PCI-MSI 346112-edge      PCIe PME, pciehp

通过这个案例可以看到多工具、多个指标对比的好处,可以发现进程内部的变化及细节。

再回到CPU上下文切换多少才算正常?这个值其实取决于CPU本身的性能,从数百到一万都可以算正常,但是当上下文切换超多一万的时候,就可能要分析系统,特别是当切换次数出现数量级的增长,就很可能出现性能问题。

可以根据上下文切换的类型,做进一步的分析

自愿上下文切换增多,说明进程等待资源,有可能出现内存、IO等其他问题

非自愿上下文切换增多,说明进程被强制调度,也就是在争夺CPU资源,CPU成了系统性能瓶颈

中断次数变多,说明CPU被中断处理程序占用,可以通过cat /proc/interrupts文件,查看中断类型

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值