四 上下文切换

一  前言

 对“平均负载”一课的学习后,你可能会有疑问,进程在竞争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

原文地址:https://www.cnblogs.com/fengzhihai/p/10071958.html