性能測試必備知識(5)- 深入理解「CPU 上下文切換」
- 2020 年 7 月 22 日
- 筆記
- 性能測試必備知識, 測試高級進階技能系列 - 性能測試必備知識
做性能測試的必備知識系列,可以看下面鏈接的文章哦
//www.cnblogs.com/poloyy/category/1806772.html
前言
上一篇文章中,舉例了大量進程等待 CPU 調度的場景
靈魂拷問
既然進程是在等待,並沒有運行,為什麼系統的平均負載還是會升高呢
回答
本文的重點:CPU 上下文切換就是罪魁禍首
先來聊聊 Linux
提出疑問
- 之前說最好一個 CPU 運行一個進程,這樣 CPU 利用率剛剛好
- 但事實上我們的 Linux 會同時運行很多進程,包括系統態的和自己啟動的進程,這不就違背了我們的美好初衷嗎?
知識點來回答疑問
- Linux 是一個多任務作業系統
- 它支援遠大於 CPU 數量的任務同時運行
- 但多任務其實並不是真的在同時運行
- 而是因為系統在很短時間內,將 CPU 輪流分配給它們,造成多任務同時運行的錯覺
什麼是 CPU 上下文
CPU 暫存器和程式計數器(PC)
- 在每個任務運行前,CPU 都需要知道任務從哪裡載入、又從哪裡開始運行
- 所以需要系統事先幫它設置好 CPU 暫存器和程式計數器
CPU 暫存器
CPU 內置的容量小,但速度極快的記憶體
程式計數器
用來存儲 CPU 正在執行的指令位置,或者即將執行的下一條指令位置
CPU 上下文
CPU 暫存器和程式計數器是 CPU 在運行任何任務前,必須的依賴環境,所以也被叫做 CPU 上下文
CPU 上下文切換
分步驟去理解
- 先把前一個任務的 CPU 上下文(CPU 暫存器和程式計數器)保存起來
- 載入新任務的上下文到這些暫存器和程式計數器
- 最後再跳轉到程式計數器所指的新位置,運行新任務
- 保存下來的上下文,會存儲到系統內核中,並在任務重新調度執行時再次載入進來,這樣能保證任務原來的狀態不受影響,讓任務看起來還是連續運行
靈魂拷問一
CPU 上下文切換無非就是更新了 CPU 暫存器的值嘛,但這些暫存器,本身就是為了快速運行任務而設計的,為什麼會影響系統的 CPU 性能呢?
靈魂拷問二
- 上面老說到的【任務】到底是什麼呢?
- 是進程,執行緒?是的,進程和執行緒是最常見的任務
- 那除此之外,還有其他的任務嗎?
回答
硬體通過觸發訊號,會導致中斷處理程式的調用,也是一種常見的任務
所以,根據任務的不同,CPU 的上下文切換可以分為不同的場景
- 進程上下文切換
- 執行緒上下文切換
- 中斷上下文切換
系統調用
Linux 按照特權等級劃分進程的運行空間
分為
- 內核空間(Ring 0):具有最高許可權,可以直接訪問所有資源
- 用戶空間(Ring 3):只能訪問受限資源,不能直接訪問記憶體等硬體設備,必須通過系統調用陷入到內核中,才能訪問這些特權資源
也就是說,進程既可以在用戶空間運行,稱為進程的用戶態
又可以在內核空間運行,稱為進程的內核態
重點:用戶態到內核態的轉變需要通過系統調用來完成
系統調用的栗子
比如,當我們查看文件內容時,就需要多次系統調用來完成:
- 首先調用 open() 打開文件
- 然後調用 read() 讀取文件內容
- 並調用 write() 將內容輸出
- 最後調用 close() 關閉文件
系統調用的過程發生 CPU 上下文的切換
- CPU 暫存器里原來用戶態的指令位置,需要先保存起來
- 為了執行內核態程式碼,CPU 暫存器需要更新為內核態指令的新位置
- 最後才是跳轉到內核態運行內核任務
- 系統調用結束後,CPU 暫存器需要恢復原來保存的用戶態
- 然後再切換回用戶空間,繼續運行進程
總結下
- 一次系統調用的過程,其實發生了兩次 CPU 上下文切換(用戶態切內核態,內核態再切回用戶態)
- 系統調用過程中,並不會涉及到虛擬記憶體等進程用戶態的資源,也不會切換進程
和進程上下文切換的不同
- 進程上下文切換:從一個進程切換到另一個進程運行
- 系統調用:一直是同一個進程在運行
總結
- 系統調用過程通常稱為特權模式切換,而不是上下文切換
- 但實際上,系統調用過程中, CPU 上下文切換是無法避免的
進程上下文切換
基礎知識點
- 在 Linux 中,進程是由內核來管理和調度
- 進程的切換只能發生在內核態
- 所以,進程的上下文不僅包括了虛擬記憶體、棧、全局變數等用戶空間的資源,還包括了內核堆棧、暫存器等內核空間的狀態
進程的上下文切換就比系統調用時多了一步
- 在保存當前進程的內核狀態和 CPU 暫存器之前,需要先把該進程的虛擬記憶體、棧等保存下來【保存上下文】
- 而載入了下一進程的內核態後,還需要刷新進程的虛擬記憶體和用戶棧【載入上下文】
保存上下文和載入上下文的過程需要內核在 CPU 上運行才能完成
進程上下文切換如何影響系統性能?
- 根據 Tsuna 的測試報告,每次上下文切換都需要幾十納秒到數微秒的 CPU 時間
- 這個時間還是略大的,特別是在進程上下文切換次數較多的情況下,很容易導致 CPU 將大量時間耗費在暫存器、內核棧以及虛擬記憶體等資源的保存和恢復上,進而大大縮短了真正運行進程的時間
- 這也正是上一篇文章中講到的,導致平均負載升高的一個重要因素
TLB 也會受影響?
- TLB(Transaction Lookaside Buffer)來管理虛擬記憶體到物理記憶體的映射關係
- 當虛擬記憶體更新後,TLB 也需要刷新,記憶體的訪問也會變慢
- 特別是在多處理器系統上,快取是被多個處理器共享的,刷新快取不僅會影響當前處理器的進程,還會影響共享快取的其他處理器的進程
什麼時候會切換進程上下文
- 顧名思義,只有在進程切換時才需要切換上下文
- 換句話說,只有在進程調度時才需要切換上下文
CPU 如何挑選進程來運行?
- Linux 為每個 CPU 都維護了一個等待隊列
- 將活躍進程(正在運行和正在等待 CPU 的進程)按照優先順序和等待 CPU 的時間排序
- 然後選擇最需要 CPU 的進程,也就是優先順序最高和等待 CPU 時間最長的進程來運行
進程什麼時候才會被調度到 CPU 上運行?
1 – 主動釋放
進程執行完終止了,會釋放 CPU,這時候從等待隊列中拿一個新的進程來運行
2 – 時間片輪轉
為了保證所有進程可以得到公平調度,CPU 時間被劃分為一段段的時間片,這些時間片再被輪流分配給各個進程,當某個進程的時間片耗盡了,就會被系統掛起,切換到其它正在等待 CPU 的進程運行
3 – 資源不足
進程在系統資源不足(比如記憶體不足)時,要等到資源滿足後才可以運行,這個時候 進程也會被掛起,並由系統調度其他進程運行
4 – sleep 函數
當進程通過睡眠函數 sleep 這樣的方法將自己主動掛起時,自然也會重新調度
5 – 優先順序高
當有優先順序更高的進程運行時,為了保證高優先順序進程的運行,當前進程會被掛起,由高優先順序進程來運行
6 – 硬中斷
發生硬體中斷時,CPU 上的進程會被中斷掛起,轉而執行內核中的中斷服務程式
執行緒上下文切換
先來聊下執行緒和進程的關係
- 執行緒和進程的最大區別在於:執行緒是調度的基本單位,進程是資源分配的基本單位
- 內核中的任務調度,實際上的調度對象是執行緒
- 而進程只是給執行緒提供了虛擬記憶體、全局變數等資源
- 當進程只有一個執行緒時,可以任務進程=執行緒
- 當進程有多個執行緒時,執行緒會共享進程的虛擬記憶體和全局變數等資源,在執行緒上下文切換時這些資源是不需要修改的
- 執行緒也有獨立的數據,比如棧、暫存器等,這些在執行緒上下文切換時是需要保存的
執行緒上下文切換的場景一
- 前後兩個執行緒屬於不同進程
- 此時,因為不同進程的資源不共享,所以執行緒上下文切換 等同於 進程上下文切換
執行緒上下文切換的場景二
- 前後兩個執行緒屬於同一個進程
- 此時,因為同一進程的資源是共享的,所以在切換時,虛擬記憶體這些資源就保持不動
- 只需要切換執行緒的私有數據、暫存器等不共享的數據
多執行緒的優勢
執行緒上下文切換對比進程上下文切換,很明顯切換消耗的資源會更少,所以多執行緒比多進程更有優勢
中斷上下文切換
中斷處理
為了快速響應硬體的事件,中斷處理會打斷進程的正常調度和執行,轉而調用中斷處理程式,響應設備事件
在打斷其他進程時,就需要將進程當前的狀態保存下來,這樣在中斷結束後,進程仍然可以從原來的狀態恢復運行
和進程上下文切換的不同點
- 中斷上下文切換並不涉及到進程的用戶態
- 即便中斷過程打斷了 一個正處在用戶態的進程,也不需要保存和恢復這個進程的虛擬記憶體、全局變數等用戶態資源
- 中斷上下文,只包括內核態中斷服務程式執行所必需的狀態,包括 CPU 暫存器、內核堆棧、硬體中斷參數
中斷上下文不會和進程上下文切換同時發生
- 對同一個 CPU 來說,中斷處理比進程擁有更高的優先順序
- 由於中斷會打斷正常進程的調度和執行,所以大部分中斷處理程式都短小精悍,以便儘可能快的執行結束
耗資源程度
- 跟進程上下文切換一樣,中斷上下文切換也需要消耗 CPU,切換次數過多也會耗費大量的 CPU,甚至嚴重降低系統的整體性能
- 當發現中斷次數過多時,就需要注意去排查它是否會給你的系統帶來嚴重的性能問題
CPU 上下文切換的總結
- CPU 上下文切換,是保證 Linux 系統正常工作的核心功能之一,一般情況下不需要關注【CPU 上下文切換是正常核心功能值】
- 但過多的上下文切換,會把 CPU 時間消耗在暫存器、內核棧、虛擬記憶體等數據的保存和恢復上,從而縮短進程真正的運行時間,導致系統的整體性能大幅下降【數據保存和恢復時間增加,進程運行時間減少,性能下降】