怒啃 24 小時,終於搞懂上下文切換!

大家好,我是樹哥。

對於服務器系統來說,上下文切換也是影響系統性能的一個重要因素。深入理解上下文切換的原理,有利於我們做好性能優化工作。今天我將帶大家瞭解下上下文切換的幾種情形,以及其背後發生切換的具體信息,接着介紹一些監測上下文切換指標的工具,最後總結一些上下文切換異常可能得場景。

深入理解系統上下文切換

什麼是上下文切換?

我們知道 Linux 是一個多任務操作系統,它能支持遠大於 CPU 數量的任務同時運行。但實際上同一時刻只會有 CPU 數量的進程在運行,等 CPU 時間片到了之後,進程調度器就會把 CPU 資源分配給其他進程。在這個過程中就會涉及到進程之間的切換,這時候就需要將當前進程的上下文信息保存下來,隨後加載被調度進程的上下文信息,這就是上下文切換。

這裏所說的上下文信息,既包括虛擬內存、棧、全局變量等用戶態的資源,也包括內核堆棧、寄存器等內核態的資源。不同類型的上下文切換,會涉及到不同類型資源的切換,例如:同一進程不同線程的切換,只需要切換內核態的資源,而不需要切換用戶態的資源。

上下文切換類型

上下文還分爲了三種類型,分別是:

  • 進程上下文切換
  • 線程上下文切換
  • 中斷上下文切換

進程上下文切換

進程上下文切換指的是不同進程之間發生切換。一般來說,進程被調度有如下幾個時機:

  1. 某個進程時間片耗盡,會被系統掛起,切換到其他等待 CPU 的進程。
  2. 進程所需系統資源不足,需要等到資源滿足時纔可運行,此時會被掛起,其他進程會被調度。
  3. 進程通過 sleep 方法主動掛起,其他進程就有機會被調度。
  4. 有更高優先級的進程,當前進程會被掛起,高優先級進程會被調度。
  5. 硬件中斷時,CPU 上的進程會被中斷掛起,轉而執行內核中的中斷服務程序。

當發生如上幾種情況的時候,就會發生進程調度,進而發生進程上下文切換。

線程上下文切換

我們都知道進程是資源分配的基本單位,線程是調度的基本單位,進程只是給線程提供了虛擬內存等資源。而線程上下文切換,就可以分爲兩種情況:

  • 進程調度前後的兩個線程,屬於同一進程。此時因爲資源共享,所以在切換的時候虛擬內存等這些資源就不需要變化,只需要切換線程的私有數據、寄存器等不共享的數據。
  • 進程調度前後的兩個線程,不屬於同一進程。這時候因爲資源部共享,所以切換過程和進程上下文切換是一樣的。

所以你會發現同進程內的線程切換,要比多進程間的切換消耗更少的資源,這其實就是多線程比起多進程的一個優勢。

中斷上下文切換

中斷上下文切換指的是爲了響應硬件的事件,中斷處理會打斷進程的正常調度和執行,轉而調用中斷處理程序,響應設備事件。而在打斷其他進程時,就需要將當前的狀態保存下來。這樣在中斷結束後,進程仍然可以從原來的狀態恢復運行。

中斷上下文切換,並不需要保存和恢復進程的虛擬內存等用戶態資源,只需要處理 CPU 寄存器、內核堆棧等內核態的資源即可。

分析工具

查看系統的上下文切換情況,有三個工具可以使用,分別是:vmstat 命令、pidstat 命令、/proc/interrupts 文件。

vmstat 命令

vmstat 是一個常用的系統性能分析工具,主要用來分析系統的內存使用情況,也常用來分析 CPU 上下文切換和中斷的次數。該命令的語法格式爲:

vmstat <選項> <時間間隔> <報告次數>

其中常用的選項有:

-a:顯示活動內頁;
-f:顯示啓動後創建的進程總數;
-m:顯示slab信息;
-n:頭信息僅顯示一次;
-s:以表格方式顯示事件計數器和內存狀態;
-d:報告磁盤狀態;
-p:顯示指定的硬盤分區狀態;
-S:輸出信息的單位。

我們執行 vmstat 5 命令後,會每隔 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      0 6893400   2352 563768    0    0     2   425  153   32  1  3 96  0  0

其中與上下文相關的 4 列內容如下:

  • cs(context switch)是每秒上下文切換的次數。
  • in(interrupt)則是每秒中斷的次數。
  • r(Running or Runnable)是就緒隊列的長度,也就是正在運行和等待 CPU 的進程數。
  • b(Blocked)則是處於不可中斷睡眠狀態的進程數。

可以看到上面輸出中上下文切換次數 cs 是 32 次,而系統中斷次數 in 是 153 次,而就緒隊列長度 r 和不可中斷狀態進程數 b 都是 0。

pidstat 命令

vmstat 只給出了系統總體的上下文切換情況,要想查看每個進程的詳細情況,就需要使用我們前面提到過的 pidstat 了。給它加上 -w 選項,你就可以查看每個進程上下文切換的情況了。 例如執行如下命令,我們可以得到每個進程的上下文切換情況了。

// 每隔 5 秒輸出一次結果
// -w 表示顯示每個進程的上下文切換情況
[root@iZwz92ezhi90syoqbgjgn1Z ~]# pidstat -w 5
Linux 4.18.0-348.7.1.el8_5.x86_64 (iZwz92ezhi90syoqbgjgn1Z) 	23/08/22 	_x86_64_	(4 CPU)

15:04:39      UID       PID   cswch/s nvcswch/s  Command
15:04:44        0         1      0.20      0.00  systemd
15:04:44        0        11     27.94      0.00  rcu_sched
15:04:44        0       497     19.56      0.00  xfsaild/vda3
15:04:44        0       603      0.20      0.00  systemd-journal
15:04:44        0       829      0.40      0.00  sssd_be
15:04:44        0       831      1.60      0.00  sssd_nss
15:04:44        0      3931      0.40      0.00  kworker/2:0-mm_percpu_wq
15:04:44        0      3998      0.40      0.00  kworker/1:0-mm_percpu_wq
15:04:44        0      4005      0.20      0.00  kworker/u8:0-flush-253:0
15:04:44        0      4021      0.80      0.00  kworker/3:2-mm_percpu_wq
15:04:44        0      4037      5.99      0.00  kworker/0:0-events

上述結果中的 cswch 與 nvcswch 是重點關注的對象。cswch 表示每秒自願上下文切換(voluntary context switches)的次數,nvcswch 表示每秒非自願上下文切換(non voluntary context switches)的次數。

所謂自願上下文切換,是指進程無法獲取所需資源,導致的上下文切換。比如說, I/O、內存等系統資源不足時,就會發生自願上下文切換。而非自願上下文切換,則是指進程由於時間片已到等原因,被系統強制調度,進而發生的上下文切換。比如說,大量進程都在爭搶 CPU 時,就容易發生非自願上下文切換。

/proc/interrupts 文件

我們可以通過 vmstat 獲取中斷的次數,但是我們卻無法獲取中斷類型。實際上我們可以通過 /proc/interrupts 文件獲取中斷的詳細信息。/proc 實際上是 Linux 的一個虛擬文件系統,用於內核空間與用戶空間之間的通信。/proc/interrupts 就是這種通信機制的一部分,提供了一個只讀的中斷使用情況。

我們可以通過如下命令,動態觀察中斷的變化情況:

# -d 參數表示高亮顯示變化的區域
$ watch -d cat /proc/interrupts
           CPU0       CPU1
...
RES:    4385721    4430589    3732298    4259089   Rescheduling interrupts
...

通過這種方式,我們就可以知道具體是哪種中斷類型出現異常,從而定位到具體的資源。

如何排查異常?

今天我們深入瞭解了一下上下文切換這個指標,但每秒上下文切換多少次纔算正常呢?

這個數值取決於系統本身的 CPU 性能。如果系統的上下文切換次數比較穩定,那麼從數百到一萬以內,都應該算是正常的。但當上下文切換次數超過一萬次,或者切換次數出現數量級的增長時,就很可能已經出現了性能問題。

具體遇到問題的時候,需要根據變化的上下文切換類型,再做具體的分析。例如:

  • 自願上下文切換變多了,說明進程都在等待資源,有可能發生了 I/O 等其他問題。
  • 非自願上下文切換變多了,說明進程都在被強制調度,也就是都在爭搶 CPU,說明 CPU 的確成了瓶頸。
  • 中斷次數變多了,說明 CPU 被中斷處理程序佔用,還需要通過查看 /proc/interrupts 文件來分析具體的中斷類型。

總結

首先,我們先介紹了上下文切換的概念,以及上下文切換可能會包含用戶態資源和內核態資源。

接着,我們介紹了三種上下文切換類型,即:進程上下文切換、線程上下文切換、中斷上下文切換,並分析其切換包括的資源類型。

接着,我們介紹了 vmstat 命令、pidstat 命令、/proc/interrupts 三種分析工具。其中 vmstat 命令用戶查看系統整體上下文切換情況,pidstat 命令用戶查看進程的上下文切換情況,/proc/interrupts 文件用戶查看中斷類型及詳細情況。

最後,解釋了合理的上下文切換次數應該是數百到一萬每秒以內。但具體的問題排查,還需要根據上下文切換類型去分析。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章