Linux性能優化實戰之CPU——CPU基礎篇

一、平均負載

1、基礎知識

$ uptime
02:34:03 up 2 days, 20:14,  1 user,  load average: 0.63, 0.83, 0.88

0.63、0.83,、0.88:過去1、5、15分鐘的平均負載

簡單來說,平均負載是指單位時間內,系統處於可運行狀態和不可中斷狀態的平均進程數,也就是平均活躍進程數,它和 CPU 使用率並沒有直接關係。可運行狀態的進程,是指正在使用 CPU 或者正在等待 CPU 的進程,也就是我們常用 ps 命令看到的,處於 R 狀態(Running 或 Runnable)的進程。不可中斷狀態的進程則是正處於內核態關鍵流程中的進程,並且這些流程是不可打斷的,比如最常見的是等待硬件設備的 I/O 響應,也就是我們在 ps 命令中看到的 D 狀態(Uninterruptible Sleep,也稱爲 Disk Sleep)的進程。
2、CPU使用率
平均負載是指單位時間內,處於可運行狀態和不可中斷狀態的進程數。所以,它不僅包括了正在使用 CPU 的進程,還包括等待 CPU 和等待 I/O 的進程。而 CPU 使用率,是單位時間內 CPU 繁忙情況的統計,跟平均負載並不一定完全對應。
3、平均負載案例分析

預先安裝 stress 和 sysstat 包,如 apt install stress sysstat

(1)模擬CPU密集型
第一個終端輸入stress --cpu 1 --timeout 600用於模擬CPU滿載的場景,在第二個終端輸入watch -d uptime,可以看到平均負載的動態變化。
在這裏插入圖片描述
在第三個終端輸入mpstat -P ALL 5

-P ALL 表示監控所有CPU,後面數字5表示間隔5秒後輸出一組數據
在這裏插入圖片描述
那麼如何查看導致CPU1滿載的進程呢?第三個終端輸入pidstat -u 5 1可以看到是stress命令導致的

在這裏插入圖片描述(2)模擬IO密集型
第一個終端輸入stress -i 1 --timeout 600 ,第二個終端輸入watch -d uptime,第三個終端輸入mpstat -P ALL 5 1,再輸入pidstat -u 5 1查看導致的進程

-i 表示IO模擬

二、CPU上下文切換

在每個任務運行前,CPU 都需要知道任務從哪裏加載、又從哪裏開始運行,也就是說,需要系統事先幫它設置好 CPU 寄存器和程序計數器(Program Counter,PC)。CPU 寄存器,是 CPU 內置的容量小、但速度極快的內存。而程序計數器,則是用來存儲 CPU 正在執行的指令位置、或者即將執行的下一條指令位置。它們都是 CPU 在運行任何任務前,必須的依賴環境,因此也被叫做 CPU 上下文
CPU 上下文切換,就是先把前一個任務的 CPU 上下文(也就是 CPU 寄存器和程序計數器)保存起來,然後加載新任務的上下文到這些寄存器和程序計數器,最後再跳轉到程序計數器所指的新位置,運行新任務。

根據任務的不同,CPU 的上下文切換就可以分爲幾個不同的場景,也就是進程上下文切換線程上下文切換以及中斷上下文切換
在這裏插入圖片描述
1、進程上下文切換
進程既可以在用戶空間運行,又可以在內核空間中運行。進程在用戶空間運行時,被稱爲進程的用戶態,而陷入內核空間的時候,被稱爲進程的內核態
從用戶態到內核態的轉變,需要通過系統調用來完成
在這裏插入圖片描述
Linux 通過 TLB(Translation Lookaside Buffer)來管理虛擬內存到物理內存的映射關係。當虛擬內存更新後,TLB 也需要刷新,內存的訪問也會隨之變慢。特別是在多處理器系統上,緩存是被多個處理器共享的,刷新緩存不僅會影響當前處理器的進程,還會影響共享緩存的其他處理器的進程

2、 線程上下文切換

說完了進程的上下文切換,我們再來看看線程相關的問題。線程與進程最大的區別在於,線程是調度的基本單位,而進程則是資源擁有的基本單位。說白了,所謂內核中的任務調度,實際上的調度對象是線程;而進程只是給線程提供了虛擬內存、全局變量等資源。所以,對於線程和進程,我們可以這麼理解:當進程只有一個線程時,可以認爲進程就等於線程。當進程擁有多個線程時,這些線程會共享相同的虛擬內存和全局變量等資源。這些資源在上下文切換時是不需要修改的。另外,線程也有自己的私有數據,比如棧和寄存器等,這些在上下文切換時也是需要保存的。這麼一來,線程的上下文切換其實就可以分爲兩種情況:第一種, 前後兩個線程屬於不同進程。此時,因爲資源不共享,所以切換過程就跟進程上下文切換是一樣。第二種,前後兩個線程屬於同一個進程。此時,因爲虛擬內存是共享的,所以在切換時,虛擬內存這些資源就保持不動,只需要切換線程的私有數據、寄存器等不共享的數據。到這裏你應該也發現了,雖然同爲上下文切換,但同進程內的線程切換,要比多進程間的切換消耗更少的資源,而這,也正是多線程代替多進程的一個優勢。

3、中斷上下文切換

除了前面兩種上下文切換,還有一個場景也會切換 CPU 上下文,那就是中斷。爲了快速響應硬件的事件,中斷處理會打斷進程的正常調度和執行,轉而調用中斷處理程序,響應設備事件。而在打斷其他進程時,就需要將進程當前的狀態保存下來,這樣在中斷結束後,進程仍然可以從原來的狀態恢復運行。跟進程上下文不同,中斷上下文切換並不涉及到進程的用戶態。所以,即便中斷過程打斷了一個正處在用戶態的進程,也不需要保存和恢復這個進程的虛擬內存、全局變量等用戶態資源。中斷上下文,其實只包括內核態中斷服務程序執行所必需的狀態,包括 CPU 寄存器、內核堆棧、硬件中斷參數等。對同一個 CPU 來說,中斷處理比進程擁有更高的優先級,所以中斷上下文切換並不會與進程上下文切換同時發生。同樣道理,由於中斷會打斷正常進程的調度和執行,所以大部分中斷處理程序都短小精悍,以便儘可能快的執行結束。另外,跟進程上下文切換一樣,中斷上下文切換也需要消耗 CPU,切換次數過多也會耗費大量的 CPU,甚至嚴重降低系統的整體性能。所以,當你發現中斷次數過多時,就需要注意去排查它是否會給你的系統帶來嚴重的性能問題。
4、如何查看上下文切換

vmstat 5

在這裏插入圖片描述

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

vmstat 只給出了系統總體的上下文切換情況,要想查看每個進程的詳細情況,就需要使用我們前面提到過的 pidstat 了。給它加上 -w 選項,你就可以查看每個進程上下文切換的情況了。
在這裏插入圖片描述

cswch ,表示每秒自願上下文切換(voluntary context switches)的次數,
nvcswch ,表示每秒非自願上下文切換(non voluntary context switches)的次數。

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

5、上下文切換模擬

sysbench --threads=10 --max-time=300 threads run

在這裏插入圖片描述如圖
1、cs列的上下文切換次數由幾千驟升到63W次
2、r列就緒列的長度達到了11 超過了本機8個核心,因此一定會造成CPU的競爭
3、us和sy這兩個CPU使用率的和接近80%,幾乎都被內核所佔用
4、in列中斷高達6000多次,說明CPU的中斷處理是一個問題。
在這裏插入圖片描述
在這裏插入圖片描述pidstat 的輸出可以發現,CPU 使用率的升高果然是 sysbench 導致的,它的 CPU 使用率已經達到了 100%。但上下文切換則是來自其他進程,包括非自願上下文切換頻率最高的 pidstat ,以及自願上下文切換頻率最高的內核線程 kworker 和 sshd。
發現了一個怪異的事兒:pidstat 輸出的上下文切換次數,加起來也就幾百,比 vmstat 的 139 萬明顯小了太多。這是怎麼回事呢?

pidstat -wt 1

在這裏插入圖片描述sysbench 進程(也就是主線程)的上下文切換次數看起來並不多,但它的子線程的上下文切換次數卻有很多。看來,上下文切換罪魁禍首,還是過多的 sysbench 線程。怎樣才能知道中斷髮生的類型呢?

沒錯,那就是從 /proc/interrupts 這個只讀文件中讀取。/proc 實際上是 Linux 的一個虛擬文件系統,用於內核空間與用戶空間之間的通信。/proc/interrupts 就是這種通信機制的一部分,提供了一個只讀的中斷使用情況。

watch -d cat /proc/interrupts

在這裏插入圖片描述
可以發現,變化速度最快的是重調度中斷(RES),這個中斷類型表示,喚醒空閒狀態的 CPU 來調度新的任務運行。這是**多處理器系統(SMP)**中,調度器用來分散任務到不同 CPU 的機制,通常也被稱爲處理器間中斷(Inter-Processor Interrupts,IPI)。

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

更多文章請關注嵌入式機器人公衆號
在這裏插入圖片描述

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