这篇文章上次修改于 899 天前,可能其部分内容已经发生变化,如有疑问可询问作者。
1 CPU 上下文切换
多进程竞争 CPU 时没有真正运行,但仍然会导致系统负载升高,原因是过多的 CPU 上下文切换会把 CPU 时间消耗在寄存器、内核栈及虚拟内存等数据等保存和恢复上,从而缩短进程正真运行的时间,导致系统等整体性能大幅度下降。
CPU 上下文切换:把前一个任务的 CPU 寄存器和程序计数器保存起来,然后加载新任务的上下文到这些寄存器和程序计数器。
程序计数器:用了存储 CPU 正在执行的指令位置。
CPU 上下文切换场景:
- 系统调用。用户态到内核态时,需要通过系统调用完成,会切换特权模式。系统调用过程中是在同一个进程中的。一次系统调用会发生两次 CPU 上下文切换:CPU 寄存器更新为内核态指令的新位置,系统调用结束后,CPU 寄存器需要恢复原来保存的用户态。
- 进程上下文切换。进程的切换只发生在内核态。进程上下文包括了虚拟内存、栈、全局变量等用户空间的资源,还包括内核堆栈、寄存器等内核空间的状态。切换过程:将本进程等虚拟内存、栈等保存下来;加载新进程的内核态,并刷新新进程等虚拟内存和用户栈。当虚拟内存更新后,TLB 也会刷新(管理虚拟内存到物理内存到映射关系),内存访问随之变慢。只有进程调度到时候,才需要切换上下文。
- 线程上下文切换。当进程拥有多个线程时,这些线程会共享虚拟内存和全局变量等资源,上下文切换时无需修改,但需要修改线程自己的私有数据,如栈和寄存器等。
- 中断上下文切换。为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件。中断上下文切换不涉及进程的用户态。中断上下文只包括内核中断服务程序执行所必需的状态,包括 CPU 寄存器、内核堆栈、硬件中断参数等。
2 查看 CPU 上下文切换
vmstat 用来分析系统的内存使用情况、CPU 上下文切换和中断次数。
# 5 表示每隔 5s 输出一组数据
$ vmstat 5
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 152576 6399908 4478320 52046368 0 0 21 45 1 0 6 0 94 0 0
- r Running or Runnable 就绪队列长度。
- b Blocked 处于不可中断睡眠状态等进程数。
- in Interrupt 每秒中断的次数
- cs Context switch 每秒上下文切换次数
查看每个进程的上下文切换情况:
$ pidstat -w 5
Linux 4.15.0-142-generic (ubuntu) 2021年09月25日 _x86_64_ (16 CPU)
17时28分14秒 UID PID cswch/s nvcswch/s Command
17时28分19秒 0 1 0.20 0.00 systemd
17时28分19秒 0 8 14.97 0.00 rcu_sched
17时28分19秒 0 11 0.40 0.00 watchdog/0
17时28分19秒 0 14 0.40 0.00 watchdog/
- cswch 每秒自愿上下文切换的次数。指进程无法获取所需的资源,导致的上下文切换。比如 I/O、内存等系统资源不足。
- nvcswch 每秒非自愿上下文切换的次数。进程由于时间片到期等原因,被系统强制调度,进而发生的上下文切换。比如大量进程争抢 CPU 时。
3 案例分析
模拟系统多线程调度瓶颈:
$ sysbench --num-threads=20 --max-time=300 --max-requests=1000000 --test=threads run
系统 cs 骤增到 290 万,in 也增加到了 1 万:
$ 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
11 0 152576 6388820 4479568 52056116 0 0 21 45 1 0 6 0 94 0 0
14 0 152576 6388564 4479568 52056116 0 0 0 0 9900 2909247 13 46 42 0 0
8 0 152576 6388564 4479568 52056116 0 0 0 0 9641 2906662 13 44 43 0 0
13 0 152576 6388564 4479568 52056116 0 0 0 0 9759 2909695 13 44 43 0 0
查看进程切换和 CPU 使用:
# -w 表示输出进程切换指标,-u 表示输出 CPU 使用指标
$ pidstat -w -u 1
$ pidstat -w -u 1
Linux 4.15.0-142-generic (ubuntu) 2021年09月25日 _x86_64_ (16 CPU)
17时56分39秒 UID PID %usr %system %guest %CPU CPU Command
17时56分40秒 1000 13311 212.87 762.38 0.00 975.25 6 sysbench
17时56分39秒 UID PID cswch/s nvcswch/s Command
17时56分40秒 0 7 12.87 0.00 ksoftirqd/0
17时56分40秒 0 8 45.54 0.00 rcu_sched
17时56分40秒 0 16 1.98 0.00 ksoftirqd/1
虽然 CPU 利用率最高的是 sysbench,但 cs 最高的并不是 sysbench,且数量较小。需要加上参数 -t 显示线程的情况:
$ pidstat -wt 5
Linux 4.15.0-142-generic (ubuntu) 2021年09月25日 _x86_64_ (16 CPU)
18时01分16秒 UID TGID TID cswch/s nvcswch/s Command
18时01分21秒 1000 - 13404 75772.31 3438.45 |__sysbench
18时01分21秒 1000 - 13405 76021.71 3480.08 |__sysbench
18时01分21秒 1000 - 13406 76695.82 3514.34 |__sysbench
18时01分21秒 1000 - 13407 76080.28 3487.45 |__sysbench
18时01分21秒 1000 - 13408 76257.37 3513.75 |__sysbench
18时01分21秒 1000 - 13409 76097.61 3487.65 |__sysbench
18时01分21秒 1000 - 13410 76530.28 3555.98 |__sysbench
18时01分21秒 1000 - 13411 75208.37 3452.79 |__sysbench
18时01分21秒 1000 - 13412 76037.25 3507.57 |__sysbench
18时01分21秒 1000 - 13413 76456.77 3538.05 |__sysbench
18时01分21秒 1000 - 13414 75866.14 3533.47 |__sysbench
18时01分21秒 1000 - 13415 75675.50 3423.71 |__sysbench
18时01分21秒 1000 - 13416 75554.78 3496.22 |__sysbench
18时01分21秒 1000 - 13417 75558.17 3468.13 |__sysbench
18时01分21秒 1000 - 13418 75677.49 3436.85 |__sysbench
18时01分21秒 1000 - 13419 75785.06 3496.41 |__sysbench
18时01分21秒 1000 - 13420 76172.31 3557.77 |__sysbench
18时01分21秒 1000 - 13421 74532.87 3437.25 |__sysbench
18时01分21秒 1000 - 13422 75826.29 3452.19 |__sysbench
18时01分21秒 1000 - 13423 77366.53 3623.90 |__sysbench
果然,sysbench 线程的上下文切换次数最高,说明上下文切换的原因是 sysbench 中过多的线程数导致的。
参考
《Linux性能优化实战》倪鹏飞.
没有评论