1. 程式人生 > 實用技巧 >Linux效能分析-CPU上下文切換

Linux效能分析-CPU上下文切換

前言

Linux效能分析-平均負載中,提到過一種導致平均負載升高的情況,就是有大量程序或執行緒等待cpu排程。

為什麼大量程序或者執行緒等待CPU排程會導致負載升高呢?

當大量程序或者執行緒等待排程時,cpu需要更加頻繁的切換任務,在切換任務的過程中,需要儲存上一個任務的context到核心中,並且恢復當前任務的context,這種儲存和恢復的操作也是需要cpu來執行的,導致cpu都消耗在了 儲存上文和恢復下文這個過程中。

除了程序和執行緒導致的上下文切換以外,硬體產生的中斷事件也會導致上下文切換。並且中斷事件的優先順序是高於執行緒和程序任務的。

這篇文章會模擬測試這種情況。

必備的工具介紹
vmstat

vmstat是一個觀測總體上下文切換狀況的命令。
下面指令可以每隔5秒輸出一組資料。

[root@localhost shell]# 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   1544 1011512   4172 1674144    0    0     5   829   39   72  1  0 99  0  0
 1  0   1544 1011380   4172 1674176    0    0     0   162  163  236  1  0 99  0  0
 0  0   1544 1011412   4172 1674176    0    0     0     0  142  240  1  0 99  0  0
 0  0   1544 1011412   4172 1674176    0    0     0     0  119  159  1  0 99  0  0
 0  0   1544 1012404   4172 1674076    0    0     0     0  533  783  1  1 97  0  0

重點關注列含義:

  • r列:就緒佇列的長度,也就是正在執行和等待cpu的任務數
  • b列:處於不可中斷睡眠狀態的任務數
  • in列:每秒中斷的次數
  • cs列:每秒上下文切換的次數
pidstat

使用vmstat關注到了整體的情況,接下來可以使用pidstat關注具體執行緒的情況
注:pidstat -wt 可以輸出執行緒的情況

[root@localhost shell]# pidstat -w 5
Linux 3.10.0-1062.el7.x86_64 (localhost.localdomain) 	2020年07月08日 	_x86_64_	(4 CPU)

13時55分20秒   UID       PID   cswch/s nvcswch/s  Command
13時55分25秒     0         9     12.48      0.00  rcu_sched
13時55分25秒     0        11      0.20      0.00  watchdog/0
13時55分25秒     0        12      0.20      0.00  watchdog/1
13時55分25秒     0        14      0.20      0.00  ksoftirqd/1
13時55分25秒     0        17      0.20      0.00  watchdog/2
13時55分25秒     0        22      0.20      0.00  watchdog/3
13時55分25秒     0        47      0.20      0.00  khugepaged
13時55分25秒     0       378      1.39      0.00  irq/16-vmwgfx
13時55分25秒     0       501      0.20      0.00  kworker/0:1H
13時55分25秒     0      1891     12.87      0.20  X
13時55分25秒     0      2175      0.20      0.00  wpa_supplicant
13時55分25秒     0      2529      0.20      0.00  ssh-agent
13時55分25秒     0      2581     14.26      0.00  gnome-shell
13時55分25秒     0      2604      0.79      0.00  ibus-daemon
13時55分25秒     0      2668      0.20      0.00  goa-identity-se
13時55分25秒     0      2756      0.99      0.00  gsd-color
13時55分25秒     0      2847     10.10      0.00  vmtoolsd
13時55分25秒     0      2947      0.20      0.00  ibus-engine-sim
13時55分25秒     0      6206      5.74      0.00  gnome-terminal-
13時55分25秒     0      6833      0.99      0.00  kworker/1:28
13時55分25秒     0     15685      0.20      0.00  fwupd
13時55分25秒     0     24573     10.10      0.00  vmtoolsd
13時55分25秒     0     24900      1.58      0.00  rngd
13時55分25秒     0    112976      1.39      0.00  kworker/3:0
13時55分25秒     0    115704      0.79      0.00  kworker/2:2
13時55分25秒     0    116180      0.99      0.00  kworker/u256:2
13時55分25秒     0    119739      3.37      0.00  kworker/0:0
13時55分25秒     0    119809      0.59      0.00  kworker/0:3
13時55分25秒     0    119826      0.20      0.00  pidstat

重點關注列含義:

  • cswch/s:自願切換上下文。比如IO記憶體等系統資源不足時,任務會主動切換上下文。
  • nvcswch/s:非自願上下文切換,當大量任務等待排程時,系統會強制任務上下cpu。
實際測試

系統環境:

作業系統:centos7
cpu:4核,記憶體:8g

首先安裝sysbench,使用sysbench,我們可以模擬一個程序內多執行緒排程引起的上下文切換問題。

[root@localhost shell]# yum install sysbench

安裝好後,執行下面命令

[root@localhost shell]# sysbench --num-threads=20 --max-time=300 --test=threads run

檢視下vmstat和pidstat

[root@localhost shell]# 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
12  0   1544 984260   4172 1700288    0    0     5   819   42   28  1  0 99  0  0
12  0   1544 984136   4172 1700288    0    0     0     2 64063 1469914 19 66 15  0  0
[root@localhost shell]# pidstat -wt 5
Linux 3.10.0-1062.el7.x86_64 (localhost.localdomain) 	2020年07月08日 	_x86_64_	(4 CPU)

14時10分21秒   UID      TGID       TID   cswch/s nvcswch/s  Command
14時10分26秒     0    119981         -      3.33      0.00  kworker/0:0
14時10分26秒     0         -    119981      3.33      0.00  |__kworker/0:0
14時10分26秒     0    120045         -      0.39      0.00  kworker/0:3
14時10分26秒     0         -    120045      0.39      0.00  |__kworker/0:3
14時10分26秒     0         -    120069  13182.55  57331.57  |__sysbench
14時10分26秒     0         -    120070  13993.33  50786.86  |__sysbench
14時10分26秒     0         -    120071  17088.63  59359.61  |__sysbench
14時10分26秒     0         -    120072  15789.22  53959.61  |__sysbench
14時10分26秒     0         -    120073  13882.35  53891.57  |__sysbench
14時10分26秒     0         -    120074  14793.33  46352.75  |__sysbench
14時10分26秒     0         -    120075  13281.18  55530.78  |__sysbench
14時10分26秒     0         -    120076  14189.80  56319.61  |__sysbench
14時10分26秒     0         -    120077  14870.39  52858.24  |__sysbench
14時10分26秒     0         -    120078  13928.04  57796.47  |__sysbench
14時10分26秒     0         -    120079  13780.39  56587.25  |__sysbench
14時10分26秒     0         -    120080  14157.65  55370.00  |__sysbench
14時10分26秒     0         -    120081  13403.14  54099.22  |__sysbench
14時10分26秒     0         -    120082  16154.31  50767.06  |__sysbench
14時10分26秒     0         -    120083  15907.45  48268.24  |__sysbench
14時10分26秒     0         -    120084  14213.53  49035.69  |__sysbench
14時10分26秒     0         -    120085  14125.29  55470.78  |__sysbench
14時10分26秒     0         -    120086  13912.16  54188.63  |__sysbench
14時10分26秒     0         -    120087  14980.59  49492.55  |__sysbench
14時10分26秒     0         -    120088  15290.39  55748.24  |__sysbench
14時10分26秒     0    120102         -      0.20      1.18  pidstat
14時10分26秒     0         -    120102      0.20      1.18  |__pidstat
[root@localhost shell]# pidstat -u 5 5
Linux 3.10.0-1062.el7.x86_64 (localhost.localdomain) 	2020年07月08日 	_x86_64_	(4 CPU)

14時31分22秒   UID       PID    %usr %system  %guest    %CPU   CPU  Command
14時31分27秒     0      1891    0.40    0.00    0.00    0.40     0  X
14時31分27秒     0      2581    0.80    0.20    0.00    1.00     0  gnome-shell
14時31分27秒     0    120701   69.26  100.00    0.00  100.00     2  sysbench
14時31分27秒     0    120722    0.00    0.20    0.00    0.20     1  pidstat

14時31分27秒   UID       PID    %usr %system  %guest    %CPU   CPU  Command
14時31分32秒     0      1891    0.40    0.20    0.00    0.60     0  X
14時31分32秒     0      2581    0.80    0.20    0.00    1.00     1  gnome-shell
14時31分32秒     0      6206    0.00    0.20    0.00    0.20     0  gnome-terminal-
14時31分32秒     0    120701   70.20  100.00    0.00  100.00     2  sysbench
14時31分32秒     0    120722    0.20    0.20    0.00    0.40     1  pidstat

觀察vmstat結果,可以看到

  • 系統整體的cs列和in列都發生了顯著的提高。
  • cpu使用率,使用者程序使用率us=19% 和系統程序使用率sy=66%,加起來達到了85%,並且sy佔用了大頭,說明更多的cpu消耗在了執行系統程序。

觀察pidstat兩類結果,可以發現

  • sysbench程序下的子執行緒,cswsh和nvcswsh的數值都很高,其中非自願切換次數遠大於自願切換次數。
  • %user(該程序在使用者空間對cpu的使用率)和%system(該程序在系統空間對cpu的使用率)更是達到了70.20和100.00,說明系統資源大部分都被這個程序所佔用。

整體結果符合我們的預期。

針對in列顯著提高,可以檢視 /proc/interrupts 檔案,裡面記錄了中斷相關的資料,這些資料記錄的是從上次啟動到現在的累加值。

我們把系統重新啟動下,看下空閒狀態下的檔案

NMI:          0          0          0          0   Non-maskable interrupts
 LOC:      28984      32366      32128      34741   Local timer interrupts
 SPU:          0          0          0          0   Spurious interrupts
 PMI:          0          0          0          0   Performance monitoring interrupts
 IWI:        353        420        266        154   IRQ work interrupts
 RTR:          0          0          0          0   APIC ICR read retries
 RES:      24371      25023      24748      22077   Rescheduling interrupts
 CAL:       6516       7288       6917       7134   Function call interrupts
 TLB:       1706       1874       1598       1534   TLB shootdowns
 TRM:          0          0          0          0   Thermal event interrupts
 THR:          0          0          0          0   Threshold APIC interrupts
 DFR:          0          0          0          0   Deferred Error APIC interrupts
 MCE:          0          0          0          0   Machine check exceptions
 MCP:          2          2          2          2   Machine check polls
 ERR:          0
 MIS:          0
 PIN:          0          0          0          0   Posted-interrupt notification event
 NPI:          0          0          0          0   Nested posted-interrupt event
 PIW:          0          0          0          0   Posted-interrupt wakeup event

當執行sysbench命令後,並執行一段時間後,該檔案如下

 NMI:          0          0          0          0   Non-maskable interrupts
 LOC:      90200      96701      93600      97704   Local timer interrupts
 SPU:          0          0          0          0   Spurious interrupts
 PMI:          0          0          0          0   Performance monitoring interrupts
 IWI:        616        666        505        450   IRQ work interrupts
 RTR:          0          0          0          0   APIC ICR read retries
 RES:    1028166    1012515    1035222    1028515   Rescheduling interrupts
 CAL:       6526       7306       6963       7134   Function call interrupts
 TLB:       1753       1946       1680       1661   TLB shootdowns
 TRM:          0          0          0          0   Thermal event interrupts
 THR:          0          0          0          0   Threshold APIC interrupts
 DFR:          0          0          0          0   Deferred Error APIC interrupts
 MCE:          0          0          0          0   Machine check exceptions
 MCP:          3          3          3          3   Machine check polls
 ERR:          0
 MIS:          0
 PIN:          0          0          0          0   Posted-interrupt notification event
 NPI:          0          0          0          0   Nested posted-interrupt event
 PIW:          0          0          0          0   Posted-interrupt wakeup event

其中,LOC和RES顯著升高
RES表示,喚醒空閒狀態的CPU來排程新的任務執行,和我們模擬的過多工排程有關。

LOC不太理解,暫時先放在這裡。