1.什么是CPU上下文切換?
linux是一個多任務操作系統,它支持遠大於CPU數量的任務同時運行,當然這些任務實際上並不是真的同時在運行,而是因為系統在很短的時間內,將CPU輪流分配給它們,造成多任務同時運行的錯覺
而在每個任務運行前,CPU都需要知道任務從哪里加載,又從哪里開始運行,也就是說需要系統事先幫它設置好CPU寄存器和程序計數器。
CPU寄存器,是CPU內置的容量小,但是速度極快的內存。而程序計數器,則是用來存儲CPU正在執行的指令位置,或者即將執行的下一條指令位置。它們都是CPU在運行任何任務前,必須依賴的環境,因此也被叫做CPU上下文
CPU的上下文切換就是先把前一個任務的CPU上下文(也就是CPU寄存器和程序計數器)保存起來,然后加載新任務的上下文到這些寄存器和程序計數器,最后再跳轉到程序計數器所指的新位置,運行新任務。
2.上下文切換的類型?(按照任務不同區分)
(1).進程上下文切換(不同進程之間上下文切換)
(2).線程上下文切換,包括同一個進程下線程上下文切換,不同進程下線程上下文切換
(3).中斷上下文切換,即中斷程序導致的上下文切換
(4).自願上下文切換(cswch),是指進程無法獲取所需資源,導致的上下文切換
(5).非自願上下文切換(nvcswch),是指進程由於時間片已到等原因,被系統強制調度,進而發生的上下文切換,比如大量進程都在爭搶CPU時,就容易發生非自願上下文切換
3.Linux中如何查看上下文切換?
vmstat中cs, 表示每秒上下文切換的次數
pidstat(pidstat -w 5),查看每個進程的上下文切換情況,cswch表示每秒自願上下文切換次數,nvcswch表示每秒非自願上下文切換的次數
4.上下文切換的場景:
(1).進程上下文切換場景:
1.為了保證所有進程得到公平調度,CPU時間被划分成一段段的時間片,這些時間片再被輪流分配給各個進程,這樣,當某個進程的時間片耗盡時,就會被系統掛起,切換到其他正在等待CPU的進程運行(即進程由於時間片已到,導致的進程上下文切換)
2.進程的系統資源不足(比如內存不足時),要等到資源滿足后才可以運行,這個時候進程會被掛起,並由系統調度其他進程運行
3.進程通過睡眠函數sleep將自己主動掛起,自然也會重新調度
4.當有優先級更高的進程運行時,為了保證高優先級進程的運行,當前進程會被掛起,由高優先級進程來運行。
5.發生硬中斷時,CPU上的進程會被中斷掛起,轉而執行內核中的中斷服務程序
進程上下文切換次數較多的情況下,很容易導致CPU將大量時間耗費在寄存器,內核棧以及虛擬內存等資源的保存和恢復上,進而大大縮短了真正運行進程的時間。這也是導致平均負載升高的一個重要因素
(2).線程上下文切換:
線程與進程的最大區別在於,線程是調度的基本單位,而進程則是資源擁有的基本單位。所謂內核中的任務調度,調度對象是線程,而不是進程,進程只是給線程提供了虛擬內存,全局變量等資源。對於進程和線程可以
這么理解:
1.當進程只有一個線程時,可以認為進程就等於線程
2.當進程擁有多個線程時,這些線程會共享相同的虛擬內存和全局變量等資源。這些資源在上下文切換時是不需要修改的。
3.線程也有自己的私有數據,比如棧和寄存器,這些在上下文切換時也是需要保存的。
線程的上下文切換就可以分為兩種情況:
1.前后兩個線程屬於不同進程,此時,因為資源不共享,所以切換過程就跟進程上下文切換一樣
2.前后兩個線程屬於同一個進程,因為虛擬內存是共享的,所以在切換時,虛擬內存這些資源就保存不動,只需要切換線程的私有數據,寄存器等不共享數據
雖然同為上下文切換,但同進程內的上下文切換,要比多進程間的切換消耗更少的資源,而這也是多線程代替多進程的一個優勢。
5.上下文切換升高導致的性能問題(案例場景):
1.准備工作:安裝sysbench,准備linux環境,開啟3個終端,切換root用戶運行
2.操作步驟:
1.在第一個終端運行sysbench,模擬多線程調度的瓶頸:
# 以 10 個線程運行 5 分鍾的基准測試,模擬多線程切換的問題
$ sysbench --threads=10 --max-time=300 threads run
2.在第二個終端運行vmstat,觀察上下文切換的情況:
# 每隔 1 秒輸出 1 組數據(需要 Ctrl+C 才結束)
$ vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
6 0 0 6487428 118240 1292772 0 0 0 0 9019 1398830 16 84 0 0 0
8 0 0 6487428 118240 1292772 0 0 0 0 10191 1392312 16 84 0 0 0
從vmstat的輸出可以發現,cs列的上下文切換次數上升到139萬,同時,注意觀察其他幾個指標:
(1).R列,就緒隊列的長度已經到了8,遠遠超過CPU個數,所以肯定會有大量的CPU競爭
(2).us和sy,這兩列的CPU使用率加起來上升到了100%,其中系統CPU使用率,也就是sy列高達84%,說明CPU主要被內核占用
(3).in列:中斷次數也上升到了1萬左右,說明中斷處理也是個潛在的問題
綜合這幾個指標,可以知道,系統的就緒隊列過長,也就是正在運行和等待CPU的進程數過多,導致了大量的上下文切換,而上下文切換又導致了系統CPU使用率升高。
那么,到底是什么進程導致了這些問題呢?
3.繼續分析,在第三個終端再用pidstat來看一下,CPU和進程上下文切換的情況:
# 每隔 1 秒輸出 1 組數據(需要 Ctrl+C 才結束)
# -w 參數表示輸出進程切換指標,而 -u 參數則表示輸出 CPU 使用指標
$ pidstat -w -u 1
08:06:33 UID PID %usr %system %guest %wait %CPU CPU Command
08:06:34 0 10488 30.00 100.00 0.00 0.00 100.00 0 sysbench
08:06:34 0 26326 0.00 1.00 0.00 0.00 1.00 0 kworker/u4:2
08:06:33 UID PID cswch/s nvcswch/s Command
08:06:34 0 8 11.00 0.00 rcu_sched
08:06:34 0 16 1.00 0.00 ksoftirqd/1
08:06:34 0 471 1.00 0.00 hv_balloon
08:06:34 0 1230 1.00 0.00 iscsid
08:06:34 0 4089 1.00 0.00 kworker/1:5
08:06:34 0 4333 1.00 0.00 kworker/0:3
08:06:34 0 10499 1.00 224.00 pidstat
08:06:34 0 26326 236.00 0.00 kworker/u4:2
08:06:34 1000 26784 223.00 0.00 sshd
從pidstat的輸出可以發現,CPU使用率升高果然是sysbench導致的,它的CPU使用率高達100%,但上下文切換則來自其他進程。
pidstat輸出的上下文切換次數,加起來也就幾百,比vmstat的139W明顯小了太多。
通過運行man pidstat,發現pidstat默認顯示的是進程的指標數據,加上 -t參數后,才會輸出線程的指標:
# 每隔 1 秒輸出一組數據(需要 Ctrl+C 才結束)
# -wt 參數表示輸出線程的上下文切換指標
$ pidstat -wt 1
08:14:05 UID TGID TID cswch/s nvcswch/s Command
...
08:14:05 0 10551 - 6.00 0.00 sysbench
08:14:05 0 - 10551 6.00 0.00 |__sysbench
08:14:05 0 - 10552 18911.00 103740.00 |__sysbench
08:14:05 0 - 10553 18915.00 100955.00 |__sysbench
08:14:05 0 - 10554 18827.00 103954.00 |__sysbench
...
從這次pidstat的輸出中,雖然sysbench進程的上下文切換次數不多,但是它的子進程上下文切換次數卻很多。看來,上下文切換罪魁禍首,還是過多的sysbench線程。
4.接下來,繼續分析中斷次數上升的根源,既然是中斷,我們都知道,它只發生在內核態,而pidstat只是一個進程的性能分析工具,並不能提供任何關於中斷的信息,怎樣才能知道中斷發生的類型呢?
從/proc/interrupts這個只讀文件讀取,/proc實際上是Linux的一個虛擬文件系統,用於內核空間與用戶空間之間的通信。/proc/interrupts就是這種通信機制的一部分,提供了一個只讀的中斷使用情況
在第三個終端,運行下面的命令,查看中斷的變化情況:
# -d 參數表示高亮顯示變化的區域
$ watch -d cat /proc/interrupts
CPU0 CPU1
...
RES: 2450431 5279697 Rescheduling interrupts
...
觀察一段時間,發現變化速度最快的是重調度中斷,這個中斷類型表示,喚醒空閑狀態的CPU來調度新的任務運行。這是多處理器系統(SMP)中,調度器用來分散任務到不同CPU的機制,通常也被稱為
處理器間中斷。所以這里的中斷升高還是因為多任務的調度問題,跟前面的上下文切換次數的分析結果是一致的。
這次通過一個sysbench的案例,講解了上下文切換問題的分析思路,碰到上下文切換次數過多的問題時,可以借助vmstat,pidstat和/proc/interrupts等工具,來輔助排查性能問題的根源。
總結:
上下文切換指標:
(1).上下文切換次數取決於系統本身的CPU性能,如果系統的上下文切換次數比較穩定,那么從數百到1萬以內都是正常的
(2).當上下文切換次數超過1萬次,或者切換次數出現數量級增長時,就很可能已經出現了性能問題
同一個進程下線程上下文切換,因為共享虛擬內存,切換過程中,虛擬內存這些資源保持不變,只需要切換線程的私有數據,寄存器等不共享的數據。(多線程代替多進程)
自願上下文切換變多了,說明進程都在等待資源,有可能發生IO等其他問題
非自願上下文切換變多了,說明進程都在被強制調度,都在爭搶CPU,說明CPU的確成了瓶頸
中斷上下文切換變多了,說明CPU被中斷處理程序占用,需要通過查看 /proc/interrupts文件分析具體的中斷類型