上下文切换小结及其带来的性能问题
一 什么是/为什么上下文切换?
1什么是上下文切换?
多个进程不停的轮流执行CPU,上下文切换是指CPU在执行完某个进程之后,需要切换到下一个进程来执行,或者当前进程申请的CPU时间片已经用完,到点儿了,轮到下一个进程来使用CPU资源了的情况下,CPU把当前进程的相关信息,比如CPU寄存器、程序计数器program counter、虚拟内存等信息暂时保存起来,转而去恢复和读取下一个进程的上下文信息,然后执行下一个进程,当下一个进程执行完之后,再来执行这个进程的时候,恢复这些刚刚保存起来的信息。
2为什么发生上下文切换?
我们看到的系统是在高并发同时执行不同的任务,其实系统内部将CPU资源分成多个时间片,让进程根据优先级高低,等待CPU时间长短来确定哪个进程来轮流使用CPU资源的。即内部通过一种快速高效的来调度进程执行,进而是用户感觉到系统是在高并发执行的,内部的这种高效切换和执行的效率实在是太高了,以至于用户或者我们根本感觉不到这种轮流切换的发生。
3为什么上下文切换不好?
因为进程申请到的可以执行的CPU时间有限,一旦把本来就不多的CPU时间,用于保存,恢复上下文的话,剩下的可以给进程真正用于执行任务的时间就不多了。所以,频繁的上下文切换,会导致性能下降。说白了,就是花费了大量的时间用于无用功上面。
4什么时候会发生上下文切换?
进程的CPU时间片用完、申请的系统资源没有获取到(比如内存不够)、进程状态转变(运行、就绪、阻塞)、sleep时间到、被硬件中断打断等。
二上下文切换的分类
1 进程上下文切换
2 线程上下文切换
3 中断上下文切换
通常来讲,如果是同一个进程下的多线程上下文切换,则线程上下文切换的开销要小于进程上下文切换。
三 动手实践线程上下文切换
1 如何查看上下文切换信息
vmstat:
vmstat 1;每秒输出一次数据,持续输出。
[root@localhost ~]# 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 6 0 0 1887908 2108 1849896 0 0 1 1 30 32 0 0 100 0 0 6 0 0 1887940 2108 1849896 0 0 0 24 45507 1495326 15 83 2 0 0 8 0 0 1887908 2108 1849896 0 0 0 0 46715 1539506 15 84 2 0 0 8 0 0 1887908 2108 1849896 0 0 0 0 52043 1428535 14 84 2 0 0 6 0 0 1887908 2108 1849896 0 0 0 0 48989 1501183 15 83 2 0 0
- r:running+runnable队列,正在运行和等待CPU的进程数;
- b(Blocked):处于不可被中断的进程队列,就是top里看到的状态为D的进程。
- in: interrupts每秒中断数;
- cs:context switch,发生上下文切换的数据;
pidstat:
pidstat -wtu 1 ; 每秒输出一次数据,-w表示输出进程切换指标,-u表示输出CPU使用指标,-t表示的是把线程上下文切换也显示出来。
[root@localhost ~]# pidstat -wtu 1 Linux 3.10.0-862.el7.x86_64 (localhost.localdomain) 2021年08月11日 _x86_64_ (2 CPU) 09时18分12秒 UID TGID TID %usr %system %guest %CPU CPU Command 09时18分13秒 0 3022 - 26.47 100.00 0.00 100.00 0 sysbench 09时18分13秒 0 - 3023 1.96 16.67 0.00 18.63 1 |__sysbench 09时18分13秒 0 - 3024 1.96 17.65 0.00 19.61 0 |__sysbench 09时18分13秒 0 - 3025 2.94 15.69 0.00 18.63 1 |__sysbench 09时18分13秒 0 - 3026 1.96 17.65 0.00 19.61 0 |__sysbench 09时18分13秒 0 - 3027 1.96 16.67 0.00 18.63 1 |__sysbench 09时18分13秒 0 - 3028 2.94 17.65 0.00 20.59 0 |__sysbench 09时18分13秒 0 - 3029 2.94 15.69 0.00 18.63 0 |__sysbench 09时18分13秒 0 - 3030 1.96 16.67 0.00 18.63 0 |__sysbench 09时18分13秒 0 - 3031 2.94 17.65 0.00 20.59 1 |__sysbench 09时18分13秒 0 - 3032 3.92 14.71 0.00 18.63 0 |__sysbench 09时18分13秒 0 3079 - 0.98 0.98 0.00 1.96 1 pidstat 09时18分13秒 0 - 3079 0.98 0.98 0.00 1.96 1 |__pidstat 09时18分12秒 UID TGID TID cswch/s nvcswch/s Command 09时18分13秒 0 9 - 12.75 0.00 rcu_sched 09时18分13秒 0 - 9 12.75 0.00 |__rcu_sched 09时18分13秒 0 11 - 0.98 0.00 watchdog/0 09时18分13秒 0 - 11 0.98 0.00 |__watchdog/0 09时18分13秒 0 12 - 0.98 0.00 watchdog/1 09时18分13秒 0 - 12 0.98 0.00 |__watchdog/1 09时18分13秒 0 14 - 4.90 0.00 ksoftirqd/1 09时18分13秒 0 - 14 4.90 0.00 |__ksoftirqd/1 09时18分13秒 0 396 - 19.61 0.00 xfsaild/dm-0 09时18分13秒 0 - 396 19.61 0.00 |__xfsaild/dm-0 09时18分13秒 0 - 1295 0.98 0.00 |__tuned 09时18分13秒 0 - 895 0.98 0.00 |__in:imjournal 09时18分13秒 0 1316 - 0.98 0.00 kworker/0:1 09时18分13秒 0 - 1316 0.98 0.00 |__kworker/0:1 09时18分13秒 0 1393 - 0.98 0.00 sshd 09时18分13秒 0 - 1393 0.98 0.00 |__sshd 09时18分13秒 1000 1455 - 0.98 0.00 postgres 09时18分13秒 1000 - 1455 0.98 0.00 |__postgres 09时18分13秒 0 3006 - 2.94 0.00 kworker/1:2 09时18分13秒 0 - 3006 2.94 0.00 |__kworker/1:2 09时18分13秒 0 - 3023 14951.96 124602.94 |__sysbench 09时18分13秒 0 - 3024 16162.75 139048.04 |__sysbench 09时18分13秒 0 - 3025 20060.78 114410.78 |__sysbench 09时18分13秒 0 - 3026 19034.31 115395.10 |__sysbench 09时18分13秒 0 - 3027 18244.12 119304.90 |__sysbench 09时18分13秒 0 - 3028 24889.22 134348.04 |__sysbench 09时18分13秒 0 - 3029 23888.24 120276.47 |__sysbench 09时18分13秒 0 - 3030 23119.61 124440.20 |__sysbench 09时18分13秒 0 - 3031 11955.88 139916.67 |__sysbench 09时18分13秒 0 - 3032 25288.24 130357.84 |__sysbench 09时18分13秒 0 3055 - 0.98 0.00 vmstat 09时18分13秒 0 - 3055 0.98 0.00 |__vmstat 09时18分13秒 0 3079 - 0.98 1.96 pidstat 09时18分13秒 0 - 3079 0.98 1.96 |__pidstat
- cswch/s:voluntary context switch自愿上下文切换,通常由于进程申请不到内存资源,或者进程等待I/O完成,但是I/O迟迟完成不了,发生了等待,这是进程自愿发生的切换;
- ncswch/s: non voluntary context switch,非自愿上下文切换,通常由于进程申请的CPU时间片使用完了,轮流到其它进程使用CPU资源的场景,即多进程竞争CPU的场景。此时,进程不得不被迫的发生切换。即进程被内核调度产生了上下文切换,所以叫做非自愿上下文切换。
2 动手实践线程上下文切换
系统环境,跑在虚拟机上的2核4G的CentOS Linux release 7.5.1804:
[root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.5.1804 (Core) [root@localhost ~]# free -m total used free shared buff/cache available Mem: 3790 132 1558 747 2099 2305 Swap: 3967 0 3967 [root@localhost ~]# lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 2 On-line CPU(s) list: 0,1 Thread(s) per core: 1 Core(s) per socket: 1 座: 2 NUMA 节点: 1 厂商 ID: AuthenticAMD CPU 系列: 16 型号: 8 型号名称: Six-Core AMD Opteron(tm) Processor 8431 步进: 0 CPU MHz: 2394.000 BogoMIPS: 4788.00 超管理器厂商: VMware 虚拟化类型: 完全 L1d 缓存: 64K L1i 缓存: 64K L2 缓存: 512K L3 缓存: 5118K NUMA 节点0 CPU: 0,1 Flags: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 syscall nx mmxext fxsr_opt pdpe1gb rdtscp lm 3dnowext 3dnow constant_tsc art rep_good nopl tsc_reliable nonstop_tsc pni cx16 x2apic popcnt hypervisor lahf_lm extapic cr8_legacy abm sse4a misalignsse 3dnowprefetch osvw retpoline_amd ibp_disable vmmcall [root@localhost ~]#
- session1:
运行 sysbench ,模拟系统多线程调度的瓶颈
[root@localhost ~]# sysbench --threads=10 --max-time=300 threads run WARNING: --max-time is deprecated, use --time instead sysbench 1.0.20 (using bundled LuaJIT 2.1.0-beta2) Running the test with following options: Number of threads: 10 Initializing random number generator from current time Initializing worker threads... Threads started!
- session2:
运行vmstat 1,查看进程上下文切换信息:
[root@localhost ~]# 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 1592800 2108 2147788 0 0 0 3 5 4 0 0 100 0 0 0 0 0 1592684 2108 2147808 0 0 0 20 85 72 0 0 100 0 0 9 0 0 1591044 2108 2147836 0 0 0 0 11126 330961 3 20 77 0 0 6 0 0 1590764 2108 2147836 0 0 0 0 49923 1544918 15 84 2 0 0 7 0 0 1590796 2108 2147844 0 0 0 0 48061 1495074 13 86 2 0 0 6 0 0 1590764 2108 2147844 0 0 0 0 49301 1512222 14 84 2 0 0 6 0 0 1590764 2108 2147844 0 0 0 0 49262 1485962 15 82 3 0 0 7 0 0 1590764 2108 2147844 0 0 0 0 48399 1533651 12 86 2 0 0 ^C [root@localhost ~]#
从上,看到cpu的r队列(已经运行CPU的和等待CPU的队列),已经满负荷在跑了,2个CPU的机器,r队列达到7个左右了。同时,system的中断in和进程上下文切换cs已经突然暴涨了。同时,CPU使用率指标上,us+sy已经逼近100%了,同时系统CPU使用率达到84%多了,说明CPU资源主要被内核占用了。
- session3:
pidstat -utw 1:查看进程的上下文切换-w;CPU使用率-u,进程的线程上下文切换-t,1表示每间隔1秒输出1次结果,并且是持续输出,直到ctrl+c结束。
[root@localhost ~]# pidstat -wut 1 Linux 3.10.0-862.el7.x86_64 (localhost.localdomain) 2021年08月15日 _x86_64_ (2 CPU) 11时49分42秒 UID TGID TID %usr %system %guest %CPU CPU Command 11时49分43秒 0 16325 - 24.51 100.00 0.00 100.00 1 sysbench 11时49分43秒 0 - 16326 1.96 15.69 0.00 17.65 1 |__sysbench 11时49分43秒 0 - 16327 3.92 15.69 0.00 19.61 1 |__sysbench 11时49分43秒 0 - 16328 3.92 18.63 0.00 22.55 0 |__sysbench 11时49分43秒 0 - 16329 2.94 16.67 0.00 19.61 0 |__sysbench 11时49分43秒 0 - 16330 2.94 18.63 0.00 21.57 1 |__sysbench 11时49分43秒 0 - 16331 2.94 15.69 0.00 18.63 1 |__sysbench 11时49分43秒 0 - 16332 1.96 18.63 0.00 20.59 0 |__sysbench 11时49分43秒 0 - 16333 1.96 18.63 0.00 20.59 1 |__sysbench 11时49分43秒 0 - 16334 0.98 16.67 0.00 17.65 1 |__sysbench 11时49分43秒 0 - 16335 1.96 15.69 0.00 17.65 0 |__sysbench 11时49分43秒 0 16372 - 0.98 0.98 0.00 1.96 0 pidstat 11时49分43秒 0 - 16372 0.98 0.98 0.00 1.96 0 |__pidstat 11时49分42秒 UID TGID TID cswch/s nvcswch/s Command 11时49分43秒 0 3 - 2.94 0.00 ksoftirqd/0 11时49分43秒 0 - 3 2.94 0.00 |__ksoftirqd/0 11时49分43秒 0 9 - 9.80 0.00 rcu_sched 11时49分43秒 0 - 9 9.80 0.00 |__rcu_sched 11时49分43秒 0 396 - 19.61 0.00 xfsaild/dm-0 11时49分43秒 0 - 396 19.61 0.00 |__xfsaild/dm-0 11时49分43秒 0 - 635 0.98 0.00 |__gmain 11时49分43秒 0 - 1295 0.98 0.00 |__tuned 11时49分43秒 0 - 895 0.98 0.00 |__in:imjournal 11时49分43秒 0 14154 - 0.98 0.00 kworker/0:0 11时49分43秒 0 - 14154 0.98 0.00 |__kworker/0:0 11时49分43秒 0 16236 - 0.98 0.00 kworker/1:0 11时49分43秒 0 - 16236 0.98 0.00 |__kworker/1:0 11时49分43秒 0 - 16326 21296.08 132564.71 |__sysbench 11时49分43秒 0 - 16327 12213.73 149473.53 |__sysbench 11时49分43秒 0 - 16328 10559.80 126749.02 |__sysbench 11时49分43秒 0 - 16329 20205.88 120006.86 |__sysbench 11时49分43秒 0 - 16330 15943.14 125617.65 |__sysbench 11时49分43秒 0 - 16331 19605.88 119339.22 |__sysbench 11时49分43秒 0 - 16332 22058.82 106377.45 |__sysbench 11时49分43秒 0 - 16333 24408.82 126319.61 |__sysbench 11时49分43秒 0 - 16334 27084.31 104035.29 |__sysbench 11时49分43秒 0 - 16335 24101.96 105334.31 |__sysbench 11时49分43秒 0 16372 - 0.98 3.92 pidstat 11时49分43秒 0 - 16372 0.98 3.92 |__pidstat
- session4:
查看中断情况:watch -d ‘cat /proc/interrupts | sort -nr -k 2’
[root@localhost ~]# watch -d 'cat /proc/interrupts | sort -nr -k 2' Every 2.0s: cat /proc/interrupts | sort -nr -k 2 Sun Aug 15 11:55:33 2021 RES: 37946619 38347726 Rescheduling interrupts LOC: 31751877 32825938 Local timer interrupts 14: 914353 13 IO-APIC-edge ata_piix 58: 866870 636678 PCI-MSI-edge ens192-rxtx-1 IWI: 365984 382747 IRQ work interrupts TLB: 91030 92900 TLB shootdowns 56: 67176 143747 PCI-MSI-edge vmw_pvscsi CAL: 57724 48417 Function call interrupts MCP: 3121 3121 Machine check polls PMI: 525 594 Performance monitoring interrupts NMI: 525 594 Non-maskable interrupts
这里,看到虚拟文件系统/proc下的中断也在明显升高,且变化最明显的是RES,重调度中断,该中断是唤醒空闲状态的CPU来调度新的任务去执行,用于多处理器(SMP)系统中,调度器用于分散不同任务到不同CPU执行的机制,也叫处理器间中断(Inter-Processor Interrupts)。
当然,此时的top看到系统CPU资源利用率非常高:
[root@localhost ~]# top top - 09:46:08 up 29 days, 23:00, 3 users, load average: 3.36, 0.95, 0.36 Tasks: 127 total, 1 running, 126 sleeping, 0 stopped, 0 zombie %Cpu0 : 11.4 us, 86.6 sy, 0.0 ni, 2.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st %Cpu1 : 11.0 us, 87.7 sy, 0.0 ni, 1.3 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 3881344 total, 2634024 free, 269276 used, 978044 buff/cache KiB Swap: 4063228 total, 4063228 free, 0 used. 2782968 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1198 root 20 0 87344 4480 3332 S 195.3 0.1 1:35.74 sysbench --threads=10 --max-time=300 threads run 1 root 20 0 125616 4100 2536 S 0.0 0.1 1:19.63 /usr/lib/systemd/systemd --switched-root --system --deserialize 22 2 root 20 0 0 0 0 S 0.0 0.0 0:01.49 [kthreadd] 3 root 20 0 0 0 0 S 0.0 0.0 0:20.05 [ksoftirqd/0] 5 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kworker/0:0H] 7 root rt 0 0 0 0 S 0.0 0.0 0:03.36 [migration/0] 8 root 20 0 0 0 0 S 0.0 0.0 0:00.00 [rcu_bh] 9 root 20 0 0 0 0 S 0.0 0.0 35:39.50 [rcu_sched] 10 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [lru-add-drain] 11 root rt 0 0 0 0 S 0.0 0.0 0:22.55 [watchdog/0] 12 root rt 0 0 0 0 S 0.0 0.0 0:20.39 [watchdog/1] 13 root rt 0 0 0 0 S 0.0 0.0 0:03.41 [migration/1] 14 root 20 0 0 0 0 S 0.0 0.0 0:12.13 [ksoftirqd/1] 16 root 0 -20 0 0 0 S 0.0 0.0 0:00.00 [kworker/1:0H] 18 root 20 0 0 0 0 S 0.0 0.0 0:00.00 [kdevtmpfs]
四小结
vmstat查看CPU的r,b队列,CPU利用率,系统的中断in和进程上下文切换指标cs;
pidstat -wut 1; 每隔1秒持续输出进程上下文切换、CPU利用率,以及线程上下文切换指标。
通常如果:
自愿上下文切换过高,说明进程在等待资源,比如可能出现了IO等待问题,导致进程资源上下文切换;
非自愿上下文切换过高,说明进程不断被系统调度,也就是说进程在争抢CPU资源,说明当前情况下CPU成了瓶颈;
中断变高,意味着CPU被中断处理程序占用,结合/proc/interruptes来分析中断类型;
五参考
https://www.howtoforge.com/how-to-benchmark-your-system-cpu-file-io-mysql-with-sysbench
https://towshif.github.io/site/tutorials/Linux%20Shell/benchmark-linux/