歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux教程 >> Linux性能分析——上下文切換

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

日期:2017/2/28 13:50:46   编辑:Linux教程

一、從一個問題說起

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

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

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

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

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

 ~ # cat /proc/stat
cpu  635 0 2319 90669 72 0 2 0 0
cpu0 635 0 2319 90669 72 0 2 0 0
intr 267849 0 0 0 119 0 0 0 0 13 13 0 0 0 0 12 0 0 0 0 0 0 0 0 0 0 0 0 0 196280 0 0 0 0 61242 1614 0 0 4770 0 0 0 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 3779 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 444616
btime 1437983701
processes 514
procs_running 1
procs_blocked 0
softirq 135729 0 93679 2 0 0 1768 0 2 40278

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

 $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,按此規則修改。

Copyright © Linux教程網 All Rights Reserved