1. 程式人生 > >Linux效能分析——上下文切換

Linux效能分析——上下文切換

一、從一個問題說起

相信很多人在玩手機還是PC時,都曾碰到過這樣一種情況,安裝的軟體多了系統性能就變慢了,但是去檢視CPU利用率一直都低於10%,記憶體也很充足。我在近期的開發工作中就碰到了類似的情況,不同的是,系統此時只有一個測試程式和幾個睡眠的後臺程序,說明是系統,特別是驅動部分可能出現問題導致的。 從作業系統角度上分析,以下是一些比較可能的原因:

  1. 大量的中斷
    可能是在不斷磁碟讀寫,網路通訊, 也可能是模組使用不當或者硬體上出問題導致外設不斷給CPU送中斷;
  2. 系統負載高(注意:不是CPU利用率) 
    負載高表示有很多程式等待排程執行,它會導致上下文切換頻繁。
  3. 上下文切換過於頻繁 
    上下文切換是指CPU從一個程序切換到另一個程序,這個過程也是需要消耗一定時間的。如果說上下文切換過於頻繁,說明CPU用於執行程序程式碼的時間少了。第2點有提到負載高會引起上下文切換頻繁,但是上下文切換頻繁負載不一定就高。

  在以往的排查經驗中,系統性能下降主要由1引起的,在影響系統性能上表現得比較明顯;而2,3則比較隱蔽,即使數值已經異常,只要應用對實時性要求不高,最多就是響應稍慢一些,看不出有什麼不妥。因此,底層驅動開發人員一般不會去考慮2,3兩點,更別說將它作為評價系統性能的測試指標。剛好我要測試的模組對實時性要求很高,而由於系統在空閒時的上下文切換已經很頻繁,測試結果自然不佳。

二、怎麼確認上下文切換頻繁?

要解決空閒時上下文切換頻繁的問題,首先要了解下多頻繁才不正常。/proc/stat檔案包含了CPU的活動資訊,上下文切換就是其中一項,下面命令輸出的粗體大號字型所示,以ctxt開頭,它表示系統開機到目前為止的上下文切換總數。

對於分析問題而言,我們更關心每秒鐘的上下文切換次數,可通過以下命令計算。

$cat /proc/stat | grep ctxt && sleep 30 && cat /proc/stat | grep ctxt
 ctxt 211015
 ctxt 218128

每秒上下文切換次數=兩者差值/30。針對本例,每秒有237次切換,對於一個空閒的系統,這是相當不正常的。正常數值範圍一般不超過50次/秒。不過這也不是固定的,需要根據不同的系統環境決定,比如本人在嵌入式開發主要從事開發POS產品,它與作為伺服器的Linux系統相比,實際執行的服務會少很多,上下文切換自然應該更少。另外,如果要求更高的實時性,則數值範圍還應進一步縮小,本人分析的產品所定的數值範圍就要求空閒時不超過30次/秒。 上面介紹的方法在所有Linux產品上都能支援,如果系統帶 vmstat

 ,通過vmstat檢視是一個更便捷的方法。測試結果如下圖紅色方框所示。

三、上下文切換頻繁時怎麼排查?

僅靠總的上下文切換次數無法定位到哪個程序或者哪個驅動出的問題,這時就需要pidstat(在sysstat包裡)可以檢視具體到每個程序每秒的上下文切換次數,下圖是執行 $pidstat -w 1的結果。

在繼續分析上圖之前我們先談一個概念:CPU密集型的程序和IO密集型的程序。CPU密集型的程序時間片總是不夠用,CPU使用率必然升高,不需要檢視上下文切換;而IO密集型的程序,會被頻繁排程,但是每次只處理一小會,從CPU使用率是看不出異常的,這時就需要檢視上下文切換。一般來講IO密集型的程序(核心執行緒或使用者程序)主要是處理讀寫磁碟,或者網路進來的資料;但是也可能有的程序沒有任何IO互動但是表現得像IO密集型程序,比如使用如下方式:

  1. 建立一個核心執行緒,一啟動就睡眠;然後建立一個10MS的timer,每次都去喚醒這個核心執行緒。
  2. 建立一個使用者程序,通過系統呼叫陷入核心就睡眠,然後建立一個10MS的timer,每次都去喚醒這個程序。

當然,我們不會傻到直接幹這樣的事,但卻可能在無意中間接做了這樣的事情,特別是對第1條,比如

  1. 建立一個工作佇列,建立一個10MS的timer,每次去schedule_work。

  工作佇列也是由一個核心執行緒在管理,導致的結果就是該核心執行緒上下文切換將變得極為頻繁。 對於上下文切換頻繁的程序,我們的關注點就是確認它們是否是IO密集型的,以及在當前系統狀態下是否應該表現得像IO密集型程序。 比如一個處理網路資料的程序,如果在沒有資料的情況下,也表現得像IO密集型,有很高的切換上下文動作,可能這個程序或者該程序開啟的裝置驅動設計得不合理。

  有兩個核心執行緒kswapd和events(新核心改為kworker),在特定情況下會表現得像IO密集型程序。其中kswapd是用於管理虛擬記憶體的,當實體記憶體不足,需要頻繁交換虛擬記憶體,kswapd的上下文切換將明顯增多;而events用來處理工作佇列,當不斷有work進入排隊且這些work處理時間很短時,events的上下文切換會明顯增多,對於這種情況,需要分析具體是由哪個驅動引起的。

  回到pidstat的輸出,可以看到events/0的上下文切換很高,既然沒有什麼程序會導致該結果,就只可能是某個驅動檔案(ko)在不斷地排隊work。目前我沒有找到比較高效的方法可以迅速定位到每個ko檔案對work的使用情況,只能通過lsmod打出已經載入的驅動,並跟以前的系統版本對比哪些做了修改以做對比。最終定位出確實存在某個驅動建立一個10MS的timer,並且每次都schedule_work。在對該驅動優化後,再次輸入pidstat -w 1測試,可以看到所有程序的下下文切換都降得很低了。

四、附錄

1、交叉編譯sysstat 一般的板子都不會帶pidstat,需要自己下載sysstat的包編譯。好在sysstat直接使用工具鏈就能編譯。操作步驟: 進入原始碼目錄

$mkdir output
 $export SA_DIR=`pwd`/output/var/log/sa
 $export conf_dir=`pwd`/output/etc/kksysconfig
 $./configure --prefix=`pwd`/output --host=arm-none-linux-gnueabi --disable-man-group
 $make 
 $make install

將output下的bin和lib拷到板子即可。注意交叉編譯時要在configure時指名工具鏈名稱,如果你的工具鏈是arm-none-linux-gnueabi-gcc,那麼就傳遞引數--host=arm-none-linux-gnueabi,如果是arm-eabi-gcc,那麼就傳遞引數--host=arm-eabi,按此規則修改。

2.參考文獻