Linux效能優化-上下文切換
目錄
CPU上下文
CPU的平均負載升高,上下文切換是罪歸禍首
Linux支援遠大於CPU數量的任務同時執行,通過將時間分片造成同時執行的錯覺
每個任務執行前,需要設定好CPU暫存器和程式計數器Program Counter PC
CPU暫存器,是CPU內建的cache,儲存資料和指令
程式計數器,用來儲存CPU正在執行的指令位置,或者即將執行的下一條指令位置,他們都是CPU在執行任何任務
前,必須依賴的環境,因此被叫做 CPU上下文
CPU上下文切換,先把前一個任務的CPU上下文(暫存器和程式計數器)儲存起來,然後載入新任務的上下文到這些暫存器和程式計數器中,最後再跳轉到程式計數器所指的新位置,執行新任務
儲存的這些上下文,會儲存在系統核心中,並在任務重新排程執行時再次載入進來,這樣就能保證任務原來的狀態不受影響,讓任務看起來是連續的
根據任務的不同,CPU的上下文切換可以分為幾個不同的場景
1.程序上下文切換
2.執行緒上下文切換
3.中斷上下文切換
程序上下文切換
Linux 按照特權等級,把程序的執行空間分為核心空間和使用者空間,對應下圖
核心空間Ring 0 有最高許可權,可以直接訪問所有資源
使用者空間Ring 3 只能訪問受限資源,不能直接訪問記憶體等硬體裝置,必須通過系統呼叫陷入到核心
中,才能訪問這些特權資源
程序既可以在使用者空間執行,又可以在核心空間中執行,程序在使用者空間執行時,稱為程序的使用者態
而陷入核心空間時,被成為程序的核心態
從使用者態到 核心態,需要通過系統呼叫來完成,比如呼叫open,read,write等
系統呼叫需要儲存CPU暫存器相關資訊,再更新為核心態指令的新位置,接著跳轉到核心態執行核心任務,等系統呼叫結束後
CPU暫存器再恢復到使用者態,切換到使用者空間
一次系統呼叫過程,發生了 兩次 CPU上下文切換
系統呼叫過程,不會涉及到虛擬記憶體等程序使用者態的資源,也不會切換程序
1.程序上下文切換,是指從一個程序切換到另一個程序執行
2.系統呼叫過程一直是在同一個程序中執行
所以系統呼叫也被稱為 特權模式切換,而不是上下文切換,但呼叫過程中CPU的上下文切換無法避免
程序的上下文切換比系統呼叫多了一步,需要儲存該程序的虛擬記憶體,棧等資訊,載入了下一程序的核心態之後,還需要重新整理程序的虛擬記憶體和使用者棧
程序上下文切換,需要核心在CPU上執行才能完成
Linux通過TLB Translation Lookaside Buffer,來管理虛擬記憶體到實體記憶體的對映管理,當虛擬記憶體更新後,TLB也要重新整理,記憶體的訪問也會變慢,如果是在多處理器上,快取是被多個處理器共享的,重新整理快取不僅會影響當前處理器的程序,還會影響共享快取的其他處理器的程序
系統為每個CPU都維護了一個就緒佇列,將活躍程序(正在執行和正在等待CPU的程序)按照優先順序和等待CPU的時間排序,然後選擇最需要CPU的程序,也就是優先順序最高和等待CPU時間最長的程序來執行
觸發程序切換的場景
1.CPU時間片到了就會切換
2.系統在資源不足(如記憶體不足)時,等到資源滿足後才可以允許,這個時候程序也會被掛起,系統會
排程其他程序
3.當程序通過sleep函式將自己掛起,系統也會重新排程
4.有更高優先順序的程序執行時,當前程序會被掛起
5.當硬體發生中斷時,CPU上的程序會被中斷掛起
執行緒上下文切換
執行緒是排程的基本單位,而程序是資源擁有的基本單位
核心中任務排程,實際上排程的物件是執行緒
程序只是給執行緒提供了虛擬記憶體,全域性變數等資源
1.當程序只有一個執行緒時,可以認為程序就等於執行緒
2.當程序擁有多個執行緒時,這些執行緒會共享相同的虛擬記憶體和全域性變數等資源,在上下文切換時不需要修改
3.執行緒私有資料,比如棧和暫存器等,這些在上下文切換的時候需要儲存
執行緒的上下文切換分為兩種情況
1.前後兩個執行緒屬於不同程序,此時資源不共享,切換過程中就跟程序上下文切換是一樣的
2.前後兩個執行緒屬於同一個程序,此時虛擬記憶體是共享的,在切換時,虛擬記憶體這些資源就保持不動,只要切換
執行緒私有資料,暫存器等不共享的資料
中斷上下文切換
中斷會打斷程序的政策排程和執行,轉而呼叫中斷處理程式,響應裝置事件,而在打斷其他程序時,就需要將程序當前的狀態儲存下來,這樣再中斷結束後,程序仍然可以從原來的狀態恢復執行
中斷上下文切換並不涉及到程序的使用者態,即便中斷過程打斷了一個正在處理使用者態的程序,也不需要儲存和恢復這個程序的虛擬記憶體,全域性變數等使用者態資源,中斷上下文,其實只包括核心態中斷服務程式執行鎖必須的狀態,包括CPU暫存器,核心堆疊,硬體中斷引數等
對同一個CPU來說,中斷處理比程序擁有更高的優先順序,所以中斷上下文切換並不會與程序上下文切換同時發生,同樣由於中斷會打斷正常程序的排程和執行,所以大部分中斷程式都短小精悍,以便快速執行結束
中斷上下文切換也會消耗CPU,切換次數過多會耗費大量CPU,降低系統整體效能
上下文切換的兩個指標
pidstat -w 輸出之後有兩個指標
一個是 cswch 表示每秒自願上下文切換(voluntary context switches)的次數,另一個是nvcswch 表示每秒非自願上下文切換(non voluntary context switches)的次數
這種模式會導致不同的效能問題
1.自願上下文切換,是指程序無法獲得所需自願,導致上下文切換,比如I/O,記憶體等系統資源不足時,就會
發生自願上下文切換
2.非自願上下文切換,是指程序由於時間片已經到等原因,被系統強制排程,進而發生的上下文切換,比如
大量程序都在爭搶CPU時,就容易發生非自願上下文切換
//目前的場景是線上伺服器有大量的網路I/O,檢視bond0 這個程序的結果
//機器是萬M網絡卡,通過dstat -n 的結果為
-net/total-
recv send
0 0
292M 176M
291M 179M
285M 175M
291M 176M
308M 191M
284M 180M
309M 191M
//bound0 這個程序的 上下文切換情況
pidstat -p 3246 -w 1
12:35:35 PM PID cswch/s nvcswch/s Command
12:35:36 PM 3246 10.00 0.00 bond0
12:35:37 PM 3246 10.00 0.00 bond0
12:35:38 PM 3246 10.00 0.00 bond0
12:35:39 PM 3246 10.00 0.00 bond0
12:35:40 PM 3246 10.00 0.00 bond0
12:35:41 PM 3246 10.00 0.00 bond0
12:35:42 PM 3246 10.00 0.00 bond0
簡單案例
用壓測工具模擬上下文切換場景,安裝 sysbench 壓測工具
//壓測
sysbench --threads=10 --max-time=300 threads run
//通過 vmstat,或者 dstat,可以發現 中斷和上下文切換瞬間變高了
//每兩秒顯示一次,-u表示cpu使用率
pidstat -w -u -2
//找到CPU使用率高的程序,然後 -w 看上下文切換情況,-u 看使用率, -t 看執行緒指標
pidstat -p 29938 -w -u -t 2
01:04:32 PM UID TGID TID %usr %system %guest %CPU CPU Command
01:04:34 PM 0 29938 - 19.50 80.00 0.00 99.50 0 sysbench
01:04:34 PM 0 - 29938 0.00 0.00 0.00 0.00 0 |__sysbench
01:04:34 PM 0 - 29939 2.00 8.00 0.00 10.00 0 |__sysbench
01:04:34 PM 0 - 29940 2.00 8.00 0.00 10.00 0 |__sysbench
01:04:34 PM 0 - 29941 2.00 8.50 0.00 10.50 0 |__sysbench
01:04:34 PM 0 - 29942 2.00 8.00 0.00 10.00 0 |__sysbench
01:04:34 PM 0 - 29943 2.00 8.00 0.00 10.00 0 |__sysbench
01:04:34 PM 0 - 29944 2.50 8.00 0.00 10.50 0 |__sysbench
01:04:34 PM 0 - 29945 1.50 8.00 0.00 9.50 0 |__sysbench
01:04:34 PM 0 - 29946 1.50 8.00 0.00 9.50 0 |__sysbench
01:04:34 PM 0 - 29947 2.00 8.00 0.00 10.00 0 |__sysbench
01:04:34 PM 0 - 29948 2.50 7.50 0.00 10.00 0 |__sysbench
01:04:32 PM UID TGID TID cswch/s nvcswch/s Command
01:04:34 PM 0 29938 - 0.00 0.00 sysbench
01:04:34 PM 0 - 29938 0.00 0.00 |__sysbench
01:04:34 PM 0 - 29939 14419.00 108190.50 |__sysbench
01:04:34 PM 0 - 29940 14475.00 110001.00 |__sysbench
01:04:34 PM 0 - 29941 14524.50 110557.00 |__sysbench
01:04:34 PM 0 - 29942 13536.50 107055.50 |__sysbench
01:04:34 PM 0 - 29943 12553.50 111592.00 |__sysbench
01:04:34 PM 0 - 29944 10616.50 112754.00 |__sysbench
01:04:34 PM 0 - 29945 19468.00 97932.50 |__sysbench
01:04:34 PM 0 - 29946 13479.50 110462.00 |__sysbench
01:04:34 PM 0 - 29947 16443.00 100000.00 |__sysbench
01:04:34 PM 0 - 29948 19969.50 97967.50 |__sysbench
找到了sysbench這個程序是上下文切換的元凶
除了大量的上下文切換,還有很多中斷
通過 /proc/interrupts 這個只讀檔案分析
/proc是 linux的一個虛擬檔案系統,用於核心空間與使用者空間之間的通訊,/proc/interrupts 就是這種通訊機制的一部分,提供了一個只讀的中斷使用情況
通過 watch -d cat /proc/interrupts 檢視
watch -d cat interrupts
.....
NMI: 0 Non-maskable interrupts
LOC: 95780367 Local timer interrupts
SPU: 0 Spurious interrupts
PMI: 0 Performance monitoring interrupts
IWI: 1503219 IRQ work interrupts
RTR: 0 APIC ICR read retries
RES: 0 Rescheduling interrupts
CAL: 0 Function call interrupts
TLB: 0 TLB shootdowns
TRM: 0 Thermal event interrupts
THR: 0 Threshold APIC interrupts
DFR: 0 Deferred Error APIC interrupts
MCE: 0 Machine check exceptions
MCP: 2019 Machine check polls
ERR: 0
MIS: 0
PIN: 0 Posted-interrupt notification event
PIW: 0 Posted-interrupt wakeup event
我的是單CPU機器,變化最大的是
LOC: 95780367 Local timer interrupts 這一段
對於多CPU機器,可能變化最大的是RES 重排程中斷,這個中斷型別表示,喚醒空閒狀態的CPU來排程新的任務執行,這是多處理器系統SMP中,排程器用來分散任務到不同CPU的機制,也被稱為 處理器間中斷 Inter-Processor Interrupts IP
所以,這裡的中斷升高還是因為過多工的排程問題,跟前面上下文切換次數分析的結果是一致的
上下文切換數量沒有一個具體的值,取決於系統本身的CPU效能,如果系統的上下文切換次數比較穩定,那麼數量從數百 -- 一萬以內都是正常的, 但超過一萬,或者切換次數出現數量級的增長,就很可能出現效能問題了