四 上下文切换
一 前言
对“平均负载”一课的学习后,你可能会有疑问,进程在竞争cpu的时候并没有正在的运行,为何会导致系统的负载升高,其实贼魁祸首就是“上下文切换”。
我们经常说linux是一个多任务的系统,它支持很多任务同时在运行。这里的“同时”仅仅是一个相对时间,其实他并非真正的在运行,只是系统在很短的时间内,将cpu
轮流分配给他们,造成了错觉。
二 何为“上下文”
任务运行前,cpu都需要知道任务从哪里加载,从哪里开始,这对应cpu的两个概念“寄存器”和“程序计数器”。
cpu寄存器:cpu内置的容量小、但速度极快的内存
cpu计数器:cpu正在执行的指令的位置、或者即将执行的吓一条指令的位置。
而这里的从哪里加载从哪里开始就是“上下文”了。
三 何为“上下文切换”
先把前一个任务的上下文(cpu寄存器和程序计数器)保存下来,然后加载新的任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器的新位置,运行新任务。
保存的上下文会存储在系统内核中,在任务重新被调度时再加载进来,因此不会影响任务的状态,看起来还是连续的。
四 都说“任务”,何为“任务”,有哪些场景
任务:进程,线程、中断等等。
上下文切换的场景:进程上下文切换、线程上下文切换、中断上下文切换
A 进程上下文切换
两个概念:
内核空间:最高权限,可访问所有资源
用户空间:不能访问内存等硬件设备,必须通过系统调用陷入到内核中,才能访问这些资源。
进程状态:内核态进程、用户态进程。
一次系统调用涉及:保存用户态,切换到内核态,然后保存内核态,切换到用户态,所以:一次系统调用,其实发生了两次cpu上下文切换。
什么时候会切换、进程什么时候会被调度到cpu上运行
1 进程执行完成
2 cpu时间被划分为一段段时间片,这些时间片被轮流分配给各个进程,但某个进程的时间片被耗尽了,就会被挂起,切换到其他等待cpu的进程上。
3 正在运行的进程资源不足了(内存等等)
4 程序有设置sleep()这样的函数时,也会主动挂起
5 中断,硬件中断,进程会被挂起,转而执行内核中的中断服务程序。
B 线程上下文切换
进程是资源拥有的基本单位,线程是调度的基本单位。
当进程只有一个线程时,可以人为进程就等于线程。
当进程拥有多个线程时,这些线程拥有相同的虚拟内存和全局变量等资源。这些资源在上下文切换时是不需要修改的。
同进程的上下文切换、不同进程的上下文切换,而后者的切换涉及资源及全局变量等的切换,所以比前者需要消耗更多的资源。
C 中断上下文切换
中断上下文切换多为内核和硬件设备的交互,优先级最高,但中断次数过多时,要排查下问题了。
五 你学到了什么?
cpu上下文切换是必要的
过多的cpu切换,会将时间消耗在寄存器、虚拟内存、内核栈等数据的保存和恢复上,而真正的运行时间却相应变短,导致系统的整体性能大大下降。
六 案例分析
案例前,我们还是了解分析工具
vmstat、pidstat、sysbench
vmstat:系统整体的上下文切换情况
pidstat:查看每个进程的上下文切换信息
sysbench:多线程进基准测试工具,可用来模拟上下文切换过多的问题
[root@new-dev-02 ~]# 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 1 0 0 4541440 872 3273768 0 0 0 1 0 0 0 0 100 0 0 0 0 0 4541516 872 3273768 0 0 0 0 96 168 0 0 100 0 0 0 0 0 4541020 872 3273772 0 0 0 12 345 483 1 1 98 0 0 0 0 0 4541392 872 3273772 0 0 0 1 116 193 0 0 100 0 0 1 0 0 4541392 872 3273772 0 0 0 3 123 194 0 0 100 0 0
(每隔5s输出一组数据。vmstat:系统整体的上下文切换情况。)
主要看四列内容:
cs(context switch):每秒上下文切换数
in(interrupt):每秒中断数
r:正在运行和等待cpu的进程数,就绪队列的长度
b:不可中断处于睡眠状态的进程数
cs/in一般几百到1万都是正常的,是否有异常不能看数量,而是看增长,是否有数量级的增长
[root@new-dev-02 ~]# pidstat -w 5 Linux 3.10.0-327.el7.x86_64 (new-dev-02) 12/05/2018 _x86_64_ (4 CPU) 06:29:57 PM UID PID cswch/s nvcswch/s Command 06:30:01 PM 0 1 5.39 0.00 systemd 06:30:01 PM 0 2 0.20 0.00 kthreadd 06:30:01 PM 0 7 0.40 0.00 migration/0 06:30:01 PM 0 13 51.70 0.00 rcu_sched 06:30:01 PM 0 14 12.38 0.00 rcuos/0 06:30:01 PM 0 15 18.76 0.00 rcuos/1 06:30:01 PM 0 16 14.77 0.00 rcuos/2 06:30:01 PM 0 17 14.37 0.00 rcuos/3 06:30:01 PM 0 18 0.20 0.00 watchdog/0 06:30:01 PM 0 19 0.20 0.00 watchdog/1 06:30:01 PM 0 20 3.59 0.00 migration/1 06:30:01 PM 0 24 0.20 0.00 watchdog/2 06:30:01 PM 0 25 3.59 0.00 migration/2 06:30:01 PM 0 29 0.20 0.00 watchdog/3 06:30:01 PM 0 30 2.79 0.00 migration/3 06:30:01 PM 0 126 2.00 0.00 kauditd
(每隔5秒输出一组数据,查看每个进程的上下文切换信息。)
主要看两列内容:
cswch/s:自愿上下文切换,进程无法获取所需要的资源,导致的上下文切换,eg:内存、IO
nvcswch/s:非自愿上下文切换,进程由于时间片已到等原因,发生的上下文切换,大量进程在抢cpu,就容易发生非自愿上下文切换
[root@new-dev-02 ~]# sysbench --threads=16 --max-time=600 threads run WARNING: --max-time is deprecated, use --time instead sysbench 1.0.9 (using system LuaJIT 2.0.4) Running the test with following options: Number of threads: 16 Initializing random number generator from current time Initializing worker threads... Threads started!
(以16个线程运行10分钟为基准,模拟多线程切换的问题。)
[root@new-dev-02 ~]# 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 10 0 0 4528168 872 3277904 0 0 0 1 0 0 0 0 100 0 0 10 0 0 4527516 872 3277908 0 0 0 0 92235 1403011 9 83 8 0 0 9 0 0 4527656 872 3277908 0 0 0 0 86846 1216013 9 85 6 0 0 7 0 0 4527656 872 3277908 0 0 0 0 136527 1989414 9 82 9 0 0 9 0 0 4527656 872 3277908 0 0 0 8 95233 1414369 10 83 7 0 0 8 0 0 4527656 872 3277908 0 0 0 0 144712 2087200 8 83 9 0 0 9 0 0 4527656 872 3277908 0 0 0 0 145905 2079661 8 83 8 0 0 9 0 0 4527904 872 3277908 0 0 0 0 110694 1598545 8 84 8 0 0 7 0 0 4527904 872 3277908 0 0 0 0 98367 1467209 9 84 7 0 0
可以看到上下文切换骤增到几百万,而内核态高达80%以上,说明cpu主要是被内核占用了。
in中断数也上升了几个数量级,说明中断也是个问题。
[root@new-dev-02 ~]# pidstat -wt 3 Average: UID TGID TID cswch/s nvcswch/s Command Average: 0 26257 - 1.99 0.00 tmux Average: 0 - 26257 1.99 0.00 |__tmux Average: 0 - 27632 7.64 0.00 |__skynet-log-agen Average: 0 - 27634 2.99 0.00 |__skynet-log-agen Average: 0 - 27635 3.65 0.00 |__skynet-log-agen Average: 0 - 27638 1.99 0.00 |__skynet-log-agen Average: 0 - 29826 1.83 0.00 |__skynet-log-agen Average: 0 - 27845 15268.27 64202.33 |__sysbench Average: 0 - 27846 16345.85 62577.41 |__sysbench Average: 0 - 27847 14405.32 63026.08 |__sysbench Average: 0 - 27848 14869.44 64488.04 |__sysbench Average: 0 - 27849 15573.59 63047.51 |__sysbench Average: 0 - 27850 16163.79 60573.59 |__sysbench Average: 0 - 27851 16725.91 61787.04 |__sysbench Average: 0 - 27852 17136.21 59015.28 |__sysbench Average: 0 - 27853 15716.45 64353.32 |__sysbench Average: 0 - 27854 15731.56 60625.75 |__sysbench Average: 0 - 27855 13386.88 66363.62 |__sysbench Average: 0 - 27856 14435.71 58306.64 |__sysbench Average: 0 - 27857 17515.61 60852.66 |__sysbench Average: 0 - 27858 18863.46 61730.07 |__sysbench Average: 0 - 27859 15648.50 62887.04 |__sysbench Average: 0 - 27860 13665.28 64576.58 |__sysbench
-wt:输出线程的上下文切换。
Every 2.0s: cat /proc/interrupts Wed Dec 5 19:10:49 2018 CPU0 CPU1 CPU2 CPU3 0: 51 0 0 0 IO-APIC-edge timer 1: 10 0 0 0 IO-APIC-edge i8042 4: 1964 4 0 0 IO-APIC-edge serial 6: 3 0 0 0 IO-APIC-edge floppy 8: 0 0 0 0 IO-APIC-edge rtc0 9: 0 0 0 0 IO-APIC-fasteoi acpi 10: 1 0 1918801 0 IO-APIC-fasteoi virtio2 11: 33 0 0 0 IO-APIC-fasteoi uhci_hcd:usb1 12: 144 0 0 0 IO-APIC-edge i8042 14: 0 0 0 0 IO-APIC-edge ata_piix 15: 0 0 0 0 IO-APIC-edge ata_piix 24: 0 0 0 0 PCI-MSI-edge virtio1-config 25: 5383 3830244 0 0 PCI-MSI-edge virtio1-req.0 26: 0 0 0 0 PCI-MSI-edge virtio0-config 27: 296706427 0 0 0 PCI-MSI-edge virtio0-input.0 28: 2 479 0 1833 PCI-MSI-edge virtio0-output.0 NMI: 0 0 0 0 Non-maskable interrupts LOC: 621848506 631386347 475404184 554846515 Local timer interrupts SPU: 0 0 0 0 Spurious interrupts PMI: 0 0 0 0 Performance monitoring interrupts IWI: 47281241 26695606 12460055 12552363 IRQ work interrupts RTR: 0 0 0 0 APIC ICR read retries RES: 107687233 115995935 85400128 83626343 Rescheduling interrupts CAL: 1044804 4294921874 1316243 668146 Function call interrupts TLB: 3770465 5407326 4488783 4455166 TLB shootdowns TRM: 0 0 0 0 Thermal event interrupts