linux 性能自我學習 ———— cpu 切換帶來的性能損耗 [二]

前言

我們知道現在操作系統,都是多進程和多線程,那麼會有一個操作系統幫助我們去切換進程和線程,這個是要消耗cpu資源的,那麼就來了解一下cpu資源消耗情況。

正文

一般是下面幾個場景切換:

  1. 進程上下文切換

  2. 線程上下文切換

  3. 中斷上下文切換

在瞭解進程切換的時候,需要了解另外一個東西,進程的運行環境,進程的運行環境分爲內核空間和用戶空間。

linux 按照特權等級,把進程的運行空間分爲內核空間和用戶空間,分佈對應着上圖中的,cpu 特權等級ring0 和 ring3。

進程既可以在用戶態空間運行,又可以在內核空間中運行,進程在用戶空間運行時,被稱爲進程的用戶態,而陷入內核空間的時候,被稱爲進程的內核態。

舉個例子,我們需要進行讀取文件。

  1. 首先需要調用open 打開文件

  2. 然後調用read 讀取文件內容

  3. 並調用write 將內容寫到標誌輸出

  4. 然後close。

那麼這個時候cpu的上下文切換是怎麼樣的呢?

cpu 寄存器裏原來用戶態的指令位置(寄存器),先保存起來。爲了執行內核態代碼,cpu 寄存器需要更新爲內核態指令的新位置。然後就跑到了內核態執行內核任務了。

而系統調用後,cpu 寄存器需要恢復到原來保存的用戶態,然後再切換到用戶空間,繼續運行進程。

一次系統調用,那麼需要兩次cpu切換。

系統調用過程中,並不會涉及到虛擬內存等進程用戶態的資源,也不會切換進程,這和進程切換不一致。

也就是說系統調用是同一個進程,發生了cpu 上下文切換(也叫特權模式切換),而進程切換,是進程進行了切換。

那麼進程的上下文切換是怎麼樣的呢?

首先進程是由內核來管理和調度的,進程的切換隻能發生在內核態。所以,進程的上下文切換不僅包含了虛擬內存、棧、全局變量等用戶空間資源,還包含了內核堆棧、寄存器等內核空間的狀態。

因此進程的上下文就比系統調用多了一步,在保存當前進程的內核狀態和cpu寄存器之前,需要把該進程的虛擬內存,棧等保留下來;而加載下一進程的內核態後,還需要刷新進程的虛擬內存和用戶棧。

這些現在不需要過於瞭解,在我後面介紹操作系統系列的時候,會詳細描述。

只需要知道其複雜高於系統調用即可,其步驟多了保存用戶空間資源的描述等。

那麼講完了進程的上下文切換,那麼講一下線程的上下文切換。

我們知道線程纔是cpu 運行的最小單位,其實進程切換就是兩個線程來自不同的進程,那麼兩個線程來自同一進程呢?這時候的cpu 操作是怎麼樣的呢?

有一個很關鍵的知識需要了解: 線程是調度的基本單位,而進程則是資源擁有的基本單位。說白了,內核的任務調度的對象是線程;而進程提供給線程虛擬內存,全局變量等資源。

這個時候就明白了,如果線程來自同一個進程,那麼就不需要切換虛擬內存,只需要切換線程的私有數據、寄存器等不共享的數據。

那麼還有一類場景是中斷上下文切換。

爲了快速響應硬件的事件,中斷處理會打斷進程的正常調度和執行,轉而調用中斷處理程序,響應設備時間。

而在打斷其他進程時,就需要將進程狀態保存下來,這樣在中斷結束後,進程依然可以從原來的狀態恢復。

跟進程上下文不同,中斷上下文切換並不涉及到進程的用戶態。所以即便中斷過程打斷一個正處於用戶態的進程,也不需要保留和恢復進程的虛擬內存、全局變量等用戶態資源。

中斷上下文,其實只包含內核態中斷服務執行必須的狀體,包括cpu 寄存器、內核堆棧、硬件中斷參數等。

對同一個cpu來說,中斷處理比進程擁有更高的優先級,所以中斷上下文切換並不會與進程上下文切換同時發生。

同樣道理,由於中斷會打斷正常進程的調度和執行,所以大部分中斷處理程序短小精悍,以便可能快的執行結束。

另外,跟進程上下文切換一樣,中斷上下文切換也需要消耗cpu,切換次數過多也會導致消耗大量的cpu,甚至英雄降低系統的整體性能。

所以,你發現中斷次數過多時,就需要注意去排查它是否會給你的系統帶來嚴重的性能影響。

實驗

我們知道除了線程需要調用cpu外,上下文還可能導致cpu高,那麼怎麼確定是不是上下文切換導致cpu高呢?

那麼我們怎麼來定位呢?

  1. cs (context switch) 每秒上下文切換的次數

  2. in (interrupt) 則是每秒中斷的次數

  3. r (running or runnable) 是就緒隊列的長度,也就是正在運行和等待cpu的進程數。

  4. 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以內。

  1. 自願切換變多了,說明進程在等待資源,那麼是io問題

  2. 非自願切換變多了,那麼就是進程被強制調度,也就是在爭搶cpu,說明cpu到達瓶頸了。

  3. 中斷次數變多了,說明cpu被中斷處理程序佔用,還需要通過查看/proc/interrupts 文件查看。

後面兩節實戰一些例子。

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