linux 性能自我學習 ———— cpu 切換帶來的性能損耗 [二]
前言
我們知道現在操作系統,都是多進程和多線程,那么會有一個操作系統幫助我們去切換進程和線程,這個是要消耗cpu資源的,那么就來了解一下cpu資源消耗情況。
正文
一般是下面幾個場景切換:
-
進程上下文切換
-
線程上下文切換
-
中斷上下文切換
在了解進程切換的時候,需要了解另外一個東西,進程的運行環境,進程的運行環境分為內核空間和用戶空間。
linux 按照特權等級,把進程的運行空間分為內核空間和用戶空間,分布對應著上圖中的,cpu 特權等級ring0 和 ring3。
進程既可以在用戶態空間運行,又可以在內核空間中運行,進程在用戶空間運行時,被稱為進程的用戶態,而陷入內核空間的時候,被稱為進程的內核態。
舉個例子,我們需要進行讀取文件。
-
首先需要調用open 打開文件
-
然后調用read 讀取文件內容
-
并調用write 將內容寫到標志輸出
-
然后close。
那么這個時候cpu的上下文切換是怎么樣的呢?
cpu 寄存器里原來用戶態的指令位置(寄存器),先保存起來。為了執行內核態代碼,cpu 寄存器需要更新為內核態指令的新位置。然后就跑到了內核態執行內核任務了。
而系統調用后,cpu 寄存器需要恢復到原來保存的用戶態,然后再切換到用戶空間,繼續運行進程。
一次系統調用,那么需要兩次cpu切換。
系統調用過程中,并不會涉及到虛擬內存等進程用戶態的資源,也不會切換進程,這和進程切換不一致。
也就是說系統調用是同一個進程,發生了cpu 上下文切換(也叫特權模式切換),而進程切換,是進程進行了切換。
那么進程的上下文切換是怎么樣的呢?
首先進程是由內核來管理和調度的,進程的切換只能發生在內核態。所以,進程的上下文切換不僅包含了虛擬內存、棧、全局變量等用戶空間資源,還包含了內核堆棧、寄存器等內核空間的狀態。
因此進程的上下文就比系統調用多了一步,在保存當前進程的內核狀態和cpu寄存器之前,需要把該進程的虛擬內存,棧等保留下來;而加載下一進程的內核態后,還需要刷新進程的虛擬內存和用戶棧。
這些現在不需要過于了解,在我后面介紹操作系統系列的時候,會詳細描述。
只需要知道其復雜高于系統調用即可,其步驟多了保存用戶空間資源的描述等。
那么講完了進程的上下文切換,那么講一下線程的上下文切換。
我們知道線程才是cpu 運行的最小單位,其實進程切換就是兩個線程來自不同的進程,那么兩個線程來自同一進程呢?這時候的cpu 操作是怎么樣的呢?
有一個很關鍵的知識需要了解: 線程是調度的基本單位,而進程則是資源擁有的基本單位。說白了,內核的任務調度的對象是線程;而進程提供給線程虛擬內存,全局變量等資源。
這個時候就明白了,如果線程來自同一個進程,那么就不需要切換虛擬內存,只需要切換線程的私有數據、寄存器等不共享的數據。
那么還有一類場景是中斷上下文切換。
為了快速響應硬件的事件,中斷處理會打斷進程的正常調度和執行,轉而調用中斷處理程序,響應設備時間。
而在打斷其他進程時,就需要將進程狀態保存下來,這樣在中斷結束后,進程依然可以從原來的狀態恢復。
跟進程上下文不同,中斷上下文切換并不涉及到進程的用戶態。所以即便中斷過程打斷一個正處于用戶態的進程,也不需要保留和恢復進程的虛擬內存、全局變量等用戶態資源。
中斷上下文,其實只包含內核態中斷服務執行必須的狀體,包括cpu 寄存器、內核堆棧、硬件中斷參數等。
對同一個cpu來說,中斷處理比進程擁有更高的優先級,所以中斷上下文切換并不會與進程上下文切換同時發生。
同樣道理,由于中斷會打斷正常進程的調度和執行,所以大部分中斷處理程序短小精悍,以便可能快的執行結束。
另外,跟進程上下文切換一樣,中斷上下文切換也需要消耗cpu,切換次數過多也會導致消耗大量的cpu,甚至英雄降低系統的整體性能。
所以,你發現中斷次數過多時,就需要注意去排查它是否會給你的系統帶來嚴重的性能影響。
實驗
我們知道除了線程需要調用cpu外,上下文還可能導致cpu高,那么怎么確定是不是上下文切換導致cpu高呢?
那么我們怎么來定位呢?
-
cs (context switch) 每秒上下文切換的次數
-
in (interrupt) 則是每秒中斷的次數
-
r (running or runnable) 是就緒隊列的長度,也就是正在運行和等待cpu的進程數。
-
b (blocked) 則是處于不可中斷睡眠的進程數。
上圖中可以看到cs 上下文切換了大概1.4k左右,in 中斷大概接近 1k,r 是0, b 是0.
那么如何我們想知道到底是哪個進程中斷次數比較多呢?
cswch 表示每秒資源上下文切換的次數 (voluntary context switchs)
另外一個是nvcswch (none voluntary context switchs) 的次數
那么什么是自愿,什么是非自愿呢?
自愿救贖指系統無法獲取所需資源,導致上下文切換。比如i/o、內存等系統資源不足時,就會發生資源上下文切換。
而非自愿切換,則是進程由于時間片等原因,被系統強制調度進而發生的上下文切換。比如大量進程都在爭搶cpu時,就容易發生非資源上下文切換。
那么上下文切換多次次數算是正常呢?
使用sysbench 模擬線程直接的切換:
sysbench --thread=10 --max-time=300 threads run
然后vmstat 進行切換。
可以看到 cs 非常高,in 相對少,說明線程或者進程直接的切換,而不是系統中斷發生的。
r 列非常高,遠超cpu 內核2個。
然后us 和 sy,分別是33 和67,說明內核占用比較大。
雖然in 比較少,但是上升也很快,說明系統中斷也是有一部分。
那么到底是哪個進程導致的呢?
通過:
pidstat -w -u 1
可以看到sysbench cpu 190%。
那么是sysbench 的問題。
那么問題就來了,這里不管自愿還是非自愿的上下文切換次數,遠遠達不到我們看到的十幾萬次數。
這是為什么呢? 因為這是進程的切換次數,如果這個進程一直捕獲的cpu,那么哪有什么機會去獲得進程切換的機會?
那么需要這樣查看:
pidstat -wt 1
那么現在上下文切換的次數找到了,那么看下系統中斷的原因。
查看cpu 中斷的原因:
watch -d cat /proc/interrupts
可以看到res 變化很大,res 是重調度中斷,這個中斷類型表示,喚醒空閑狀態cpu來調度新的任務運行。
調度器用來分散任務到不同cpu的機制,通常也被稱為處理器間中斷。
所以中斷升高還是因為任務的調度問題,跟前面一致。
那么問題來了,多少cpu 上下文切換算是正常呢? 一般是1w以內。
-
自愿切換變多了,說明進程在等待資源,那么是io問題
-
非自愿切換變多了,那么就是進程被強制調度,也就是在爭搶cpu,說明cpu到達瓶頸了。
-
中斷次數變多了,說明cpu被中斷處理程序占用,還需要通過查看/proc/interrupts 文件查看。
結
后面兩節實戰一些例子。