Linux性能優化(十一)——CPU性能優化原理

一、CPU上下文切換

1、CPU上下文

Linux是多任務操作系統,支持遠大於CPU數量的任務同時運行。在每個任務運行前,CPU需要知道任務從哪裏加載、從哪裏開始運行,即需要系統事先爲CPU設置好CPU寄存器和程序計數器(Program Counter,PC)。CPU寄存器是CPU內置的容量小、但速度極快的內存,程序計數器是用來存儲CPU正在執行的指令位置、或者即將執行的下一條指令位置。CPU寄存器和程序計數器是CPU運行任何任務前必須的依賴環境,即CPU上下文。
Linux性能優化(十一)——CPU性能優化原理
CPU上下文會存儲在系統內核中,並在任務重新調度執行時再次加載進來,保證任務原來狀態不受影響,保持多任務的連續運行。
CPU上下文切換是先把前一個任務的CPU上下文(CPU寄存器和程序計數器)保存起來,然後加載新任務的上下文到CPU寄存器和程序計數器,最後再跳轉到程序計數器所指的新位置,運行新任務。
根據任務的不同,CPU上下文切換分爲進程上下文切換、線程上下文切換、中斷上下文切換。



2、進程上下文切換

Linux按照特權等級把進程運行空間分爲內核空間和用戶空間,分別爲CPU特權等級的Ring 0和Ring 3。內核空間(Ring 0)具有最高權限,可以直接訪問所有資源;用戶空間(Ring 3)只能訪問受限資源,不能直接訪問內存等硬件設備,必須通過系統調用陷入到內核中,才能訪問特權資源。
Linux性能優化(十一)——CPU性能優化原理
進程既可以在用戶空間運行,又可以在內核空間中運行。進程在用戶空間運行時稱爲進程的用戶態,而陷入內核空間時稱爲進程的內核態。
進程從用戶態切換到內核態需要通過系統調用來完成,會發生進程上下文切換(特權模式切換),當進程從內核態切換回用戶態時也會發生上下文切換。每次進程上下文切換都需要幾十納秒到數微秒的CPU時間,如果切換頻繁會導致CPU時間的浪費在寄存器、內核棧以及虛擬內存等資源的保存和恢復上,導致系統平均負載升高。


3、線程上下文切換

線程是調度的基本單位,而進程是資源擁有的基本單位。內核任務調度對象是線程,進程是給線程提供虛擬內存、全局變量等資源。當進程擁有多個線程時,線程間共享相同的虛擬內存和全局變量等資源。共享資源在上下文切換時不需要修改,線程自己的私有數據,比如棧和寄存器等,在上下文切換時需要保存。線程上下文切換分爲兩種:第一種,兩個線程屬於不同進程,因爲資源不共享,線程切換過程跟進程上下文切換相同;第二種,兩個線程屬於同一個進程,因爲虛擬內存共享,在線程切換時,虛擬內存等資源保持不動,只需要切換線程的私有數據、寄存器等不共享數據。進程內的線程切換比多進程間的進程切換消耗更少的資源。

4、中斷上下文切換

爲了快速響應硬件的事件,中斷處理會打斷進程的正常調度和執行,轉而調用中斷處理程序,響應設備事件。而在打斷其它進程時,需要將進程當前狀態保存下來,在中斷結束後,進程仍然可以從原來狀態恢復運行。中斷上下文切換不涉及進程用戶態,因此即便中斷過程打斷一個正處在用戶態的進程,也不需要保存和恢復進程的虛擬內存、全局變量等用戶態資源。中斷上下文只包括內核態中斷服務程序執行所必需的狀態,包括CPU寄存器、內核堆棧、硬件中斷參數等。
對同一個CPU,中斷處理比進程擁有更高的優先級,因此中斷上下文切換不會與進程上下文切換同時發生。因爲中斷會打斷正常進程調度和執行,所以大部分中斷處理程序都短小精悍,以便儘可能快的執行結束。中斷上下文切換需要消耗CPU,切換次數過多也會耗費大量CPU,嚴重降低系統整體性能。

5、系統調用

系統調用時,CPU寄存器中原來用戶態的指令位置需要先保存起來;爲了執行內核態代碼,CPU寄存器需要更新爲內核態指令的新位置,跳轉到內核態運行內核任務;而系統調用結束後,CPU寄存器需要恢復原來保存的用戶態,然後再切換到用戶空間,繼續運行進程。所以,一次系統調用的過程發生了兩次CPU上下文切換。系統調用過程中不會涉及虛擬內存等進程用戶態資源,也不會切換進程。
進程上下文切換是指從一個進程切換到另一個進程運行,而系統調用過程中一直是同一個進程在運行。系統調用過程通常稱爲特權模式切換,而不是上下文切換。系統調用過程中,CPU上下文切換無法避免。進程是由內核來管理和調度的,進程切換隻能發生在內核態。進程上下文不僅包括虛擬內存、棧、全局變量等用戶空間的資源,還包括內核堆棧、寄存器等內核空間的狀態。因此,進程上下文切換比系統調用時多一步:在保存當前進程的內核狀態和CPU寄存器前,需要先把進程的虛擬內存、棧等保存下來;而加載下一進程的內核態後,還需要刷新進程的虛擬內存和用戶棧。
進程上下文切換需要內核在CPU上運行才能完成。
Linux性能優化(十一)——CPU性能優化原理
每次上下文切換都需要幾十納秒到數微秒的CPU時間,在進程上下文切換頻繁時,容易導致CPU將大量時間耗費在寄存器、內核棧以及虛擬內存等資源的保存和恢復上,進而大大縮短真正運行進程的時間。
Linux通過TLB(Translation Lookaside Buffer)來管理虛擬內存到物理內存的映射關係。當虛擬內存更新後,TLB也需要刷新,內存訪問會隨之變慢。在多處理器系統上,L3緩存是被多個處理器共享的,刷新緩存不僅會影響當前處理器進程,還會影響共享緩存的其它處理器進程。
進程切換時才需要切換上下文,即只有在進程調度時才需要切換上下文。Linux爲每個CPU都維護一個就緒隊列,將活躍進程(即正在運行和正在等待CPU的進程)按照優先級和等待CPU的時間排序,然後選擇最需要CPU的進程,即優先級最高和等待CPU時間最長的進程來運行。
觸發進程調度的場景如下:
(1)爲了保證所有進程可以得到公平調度,CPU時間被劃分爲一段段時間片,時間片再被輪流分配給各個進程。當某個進程的時間片耗盡,就會被系統掛起,切換到其它正在等待CPU的進程運行。
(2)進程在系統資源不足(比如內存不足)時,要等到資源滿足後纔可以運行,進程會被掛起,並由系統調度其它進程運行。
(3)當進程通過睡眠函數sleep方法將自己主動掛起時,會重新調度。
(4)當有優先級更高的進程運行時,爲了保證高優先級進程的運行,當前進程會被掛起,由高優先級進程來運行。
(5)發生硬件中斷時,CPU上的進程會被中斷掛起,轉而執行內核中的中斷服務程序。











6、CPU上下文切換指標

Linux性能優化(十一)——CPU性能優化原理
cs(context switch):每秒上下文切換的次數。
in(interrupt):每秒中斷的次數。
r(Running or Runnable):就緒隊列的長度,即正在運行和等待 CPU的進程數。
b(Blocked):處於不可中斷睡眠狀態的進程數。



vmstat只給出系統總體的上下文切換情況,使用pidstat可以查看進程的具體CPU上下文切換。
pidstat -w 5可以查看每個進程上下文切換情況。
Linux性能優化(十一)——CPU性能優化原理
cswch:每秒自願上下文切換(voluntary context switches)的次數
nvcswch:每秒非自願上下文切換(non voluntary context switches)的次數。
自願上下文切換是指進程無法獲取所需資源而導致的上下文切換,如IO、內存等系統資源不足時會發生自願上下文切換。自願上下文切換變多,說明進程都在等待資源,可能發生IO等其它問題。
非自願上下文切換是指進程由於時間片已到等原因,被系統強制調度,進而發生的上下文切換。如大量進程都在爭搶CPU時容易發生非自願上下文切換。非自願上下文切換變多,說明進程都在被強制調度,都在爭搶CPU。
中斷次數變多,說明CPU被中斷處理程序佔用,還需要通過查看/proc/interrupts文件來分析具體的中斷類型。






二、CPU平均負載

1、進程狀態

R是Running或Runnable縮寫,表示進程在CPU的就緒隊列中,正在運行或者正在等待運行。
D是Disk Sleep縮寫,即不可中斷狀態睡眠(Uninterruptible Sleep),一般表示進程正在跟硬件交互,並且交互過程不允許被其它進程或中斷打斷。D狀態的進程會導致平均負載升高。
Z是Zombie縮寫,表示殭屍進程,進程已經結束,但父進程還沒有回收資源(比如進程的描述符、PID 等)。
S是Interruptible Sleep縮寫,即可中斷狀態睡眠,表示進程因爲等待某個事件而被系統掛起。當進程等待的事件發生時,會被喚醒並進入R狀態。
I是Idle縮寫,即空閒狀態,用在不可中斷睡眠的內核線程上。I狀態的進程卻不會。
T是Stopped或Traced縮寫,表示進程處於暫停或者跟蹤狀態。向一個進程發送SIGSTOP信號,會因響應SIGSTOP信號變成暫停狀態(Stopped);再向進程發送SIGCONT信號,進程又會恢復運行(如果進程是終端裏直接啓動的,則需要用fg命令,恢復到前臺運行)。
X是Dead縮寫,表示進程已經消亡,因此不會在top或者ps命令中顯示。
當iowait升高時,進程很可能因爲得不到硬件的響應,而長時間處於不可中斷狀態。不可中斷狀態是爲了保證進程數據與硬件狀態一致,並且正常情況下,不可中斷狀態在很短時間內就會結束。所以,短時的不可中斷狀態進程一般可以忽略。但如果系統或硬件發生故障,進程可能會在不可中斷狀態保持很久,甚至導致系統中出現大量不可中斷進程。
正常情況下,當一個進程創建子進程後,應該通過系統調用wait或者waitpid等待子進程結束,回收子進程的資源;而子進程在結束時,會向父進程發送SIGCHLD信號,所以,父進程還可以註冊SIGCHLD信號的處理函數,異步回收資源。如果父進程沒這麼做或是子進程執行太快,父進程還沒來得及處理子進程狀態,子進程就已經提前退出,子進程就會變成殭屍進程。通常,殭屍進程持續的時間都比較短,在父進程回收資源後就會消亡;或者在父進程退出後,由init進程回收後也會消亡。一旦父進程沒有處理子進程的終止,還一直保持運行狀態,子進程就會一直處於殭屍狀態。大量的殭屍進程會用盡PID 進程號,導致新進程不能創建,必須避免。







2、CPU平均負載簡介

平均負載是指單位時間內,系統處於可運行狀態和不可中斷狀態的平均進程數,即平均活躍進程數,與CPU使用率沒有直接關係。
可運行狀態進程是指正在使用CPU或者正在等待CPU的進程,即處於R狀態(Running 或 Runnable)的進程。
不可中斷狀態進程是正處於內核態關鍵流程(不可中斷)中的進程,如常見的等待硬件設備的I/O響應,也就是我們在 ps 命令中看到的 D 狀態(Uninterruptible Sleep,也稱爲Disk Sleep)的進程。
當一個進程向磁盤讀寫數據時,爲了保證數據的一致性,在得到磁盤迴復前,進程是不能被其它進程或者中斷打斷,此時進程處於不可中斷狀態。如果此時進程被打斷,容易出現磁盤數據與進程數據不一致問題。
可中斷狀態是系統對進程和硬件設備的一種保護機制。
平均負載實際是平均活躍進程數,因此平均負載指標需要和當前計算機系統的邏輯CPU數量一起參考。理想條件下,每個CPU都剛好運行一個進程時,每個CPU都可以得到充分利用。
平均負載指標可以使用top或uptime命令進行查看。
Linux性能優化(十一)——CPU性能優化原理
uptime觀察的三個平均值分別爲最近1min、5min、15min的平均負載,三個不同時間間隔的平均負載,提供了分析系統負載趨勢的數據來源,能夠更全面、更立體地理解目前的系統負載狀況。
(1)如果1分鐘、5分鐘、15分鐘的平均負載值基本相同,說明系統負載很平穩。
(2)如果1分鐘平均負載值遠小於15分鐘平均負載值,說明系統最近1分鐘的負載在減少,而過去15分鐘內卻有很大的負載。
(3)如果1分鐘的平均負載值遠大於15分鐘的皮冠軍負載值,說明最近1分鐘的負載在增加,可能只是臨時性的,也有可能會持續增加,所以需要持續觀察。一旦1分鐘的平均負載接近或超過邏輯CPU個數,則表明當前系統正在發生過載問題。










三、CPU使用率

1、CPU使用率簡介

平均負載是指單位時間內處於可運行狀態和不可中斷狀態的進程數。因此平均負載不僅包括正在使用CPU的進程,還包括等待CPU和等待IO的進程。
CPU使用率是單位時間內CPU使用情況的統計,以百分比的方式展示。
CPU使用率是單位時間內CPU繁忙情況的統計,跟平均負載並不一定完全對應。CPU密集型進程,使用大量CPU會導致平均負載升高,此時平均負載與CPU使用率是一致的;IO密集型進程,等待IO也會導致平均負載升高,但CPU使用率不一定很高;大量等待CPU的進程調度也會導致平均負載升高,此時的CPU使用率也會比較高。
平均負載是一個快速查看系統整體性能的指標,反映了系統的整體負載情況。但平均負載指標並不能定位系統的性能瓶頸。
平均負載高有可能是CPU密集型進程導致的;
平均負載高並不一定代表CPU使用率高,可能是IO等待時間較長;當發現平均負載高時,可以使用mpstat、pidstat輔助分析定位負載來源。




2、CPU使用率定義

CPU使用率是除了空閒時間外的其它時間佔總CPU時間的百分比。
Linux性能優化(十一)——CPU性能優化原理
通過/proc/stat數據計算得到的CPU使用率是開機以來的平均CPU使用率。
爲了計算CPU使用率,性能分析工具通常會取間隔一段時間(比如 3 秒)的兩次值,作差後,再計算出時間段內的平均CPU使用率。
Linux性能優化(十一)——CPU性能優化原理
Linux爲每個進程提供了運行情況的統計信息,即/proc/[pid]/stat。性能分析工具會根據/proc/stat和/proc/[pid]/stat計算出間隔一段時間的平均CPU使用率,不同的性能分析工具默認使用的時間間隔可能不同,因此使用多個性能分析工具對比分析時,一定要保證使用相同的間隔時間。如top默認使用3秒時間間隔,而ps使用的是進程的整個生命週期。




3、CPU使用時間

Linux作爲一個多任務操作系統,將每個CPU的時間劃分爲很短的時間片,再通過調度器輪流分配給各個任務使用,因此造成多任務同時運行的錯覺。爲了維護CPU時間,Linux通過事先定義的節拍率(內核中表示爲HZ),觸發時間中斷,並使用全局變量 Jiffies記錄開機以來的節拍數。每發生一次時間中斷,Jiffies值就加1。節拍率HZ是內核的可配選項,可以設置爲100、250、1000等。不同的系統可能設置不同數值,可以通過查詢/boot/config內核選項來查看。
grep 'CONFIG_HZ=' /boot/config-$(uname -r)
節拍率HZ是內核選項,用戶空間程序不能直接訪問。爲了方便用戶空間程序,內核提供了一個用戶空間節拍率USER_HZ,固定爲100HZ。Linux通過/proc虛擬文件系統,向用戶空間提供了系統內部狀態的信息,而/proc/stat提供的是系統的CPU和任務統計信息。
cat /proc/stat | grep ^cpu
Linux性能優化(十一)——CPU性能優化原理
第一列是CPU編號,第一行沒有編號的CPU表示所有CPU的累加。其它列表示不同場景下CPU的累加節拍數,單位是USER_HZ,即10ms(1/100 秒)。
proc/stat指標如下:
user(簡寫us)代表用戶態CPU時間,不包括nice時間,但包括guest時間。
nice(簡寫ni)代表低優先級用戶態CPU時間,進程的nice值被調整爲1-19間時的CPU時間。nice可取值範圍是-20到19,數值越大,優先級反而越低。
system(簡寫sys)代表內核態CPU時間。
idle(簡寫id)代表空閒時間,不包括等待IO的時間(iowait)。
iowait(簡寫wa)代表等待IO的CPU時間。
irq(簡寫hi)代表處理硬中斷的CPU時間。
softirq(簡寫si)代表處理軟中斷的CPU時間。
steal(簡寫st)代表當系統運行在虛擬機中的時候,被其他虛擬機佔用的CPU時間。
guest(簡寫guest)代表通過虛擬化運行其它操作系統的時間,即運行虛擬機的CPU時間。
guest_nice(簡寫gnice)代表以低優先級運行虛擬機的時間。
top輸出%Cpu行是系統的CPU使用率,默認顯示是所有CPU的平均值,按下數字1可以切換查看每個CPU使用率。
Linux性能優化(十一)——CPU性能優化原理
Linux性能優化(十一)——CPU性能優化原理
進程實時信息中每個進程都有一個%CPU列,表示進程的CPU使用率,是用戶態和內核態CPU使用率的總和,包括進程用戶空間使用的CPU、通過系統調用執行的內核空間CPU 、就緒隊列等待運行的CPU。在虛擬化環境中還包括了運行虛擬機佔用的CPU。
進程的具體CPU使用率使用pidstat查看:
Linux性能優化(十一)——CPU性能優化原理
%usr指標:用戶態CPU使用率
%system:內核態CPU使用率
%guest:運行虛擬機CPU使用率
%wait:等待CPU使用率
%CPU:總的CPU使用率
Average部分會計算2組數據的平均值。



























4、異常CPU使用率

對於某些無法解釋的高CPU使用率情況時,有可能是短時應用導致的問題。
(1)應用裏直接調用了其它二進制程序,但運行時間比較短,通過 top等工具不容易發現。
(2)應用不停地崩潰重啓,而啓動過程的資源初始化可能會佔用大量CPU資源。
短時進程查看可以使用execsnoop工具進行查看。


四、中斷

1、中斷簡介

中斷是一種異步的事件處理機制,可以提高系統的併發處理能力。由於中斷處理程序會打斷其它進程的運行,所以爲了減少對正常進程運行調度的影響,中斷處理程序就需要儘可能快地運行。如果中斷服務程序運行時間較長,特別是中斷處理程序在響應中斷時,還會臨時關閉中斷,導致上一次中斷處理完成前,其它中斷都不能響應,即中斷有可能會丟失。
爲了解決中斷處理程序執行過長和中斷丟失的問題,Linux將中斷處理過程分成兩個階段,上半部用來快速處理中斷,在中斷禁止模式下運行,主要處理跟硬件緊密相關的或時間敏感的工作,下半部用來延遲處理上半部未完成的工作,通常以內核線程的方式運行。
上半部直接處理硬件請求,即硬中斷,特點是快速執行;而下半部則是由內核觸發,即軟中斷,特點是延遲執行。硬件中斷會打斷CPU正在執行的任務,然後立即執行中斷處理程序;軟件中斷以內核線程方式執行,並且每個CPU都對應一個軟中斷內核線程,名字爲 ksoftirqd/C編號,CPU0對應的軟中斷內核線程的名字是 ksoftirqd/0。軟中斷不只包括硬件設備中斷處理程序的下半部,一些內核自定義的事件也屬於軟中斷,比如內核調度和RCU鎖(Read-Copy Update,RCU是Linux內核中最常用的鎖之一)等。
網卡接收到數據包後,會通過硬件中斷方式通知內核有新的數據,內核就調用中斷處理程序把網卡數據讀到內存中,並且更新硬件寄存器狀態(表示數據已經讀好),最後再發送一個軟中斷信號,通知下半部做進一步的處理。而下半部被軟中斷信號喚醒後,需要從內存中找到網絡數據,再按照網絡協議棧,對數據進行逐層解析和處理,直到把它送給應用程序。
Linux中軟中斷包括網絡收發、定時、調度、RCU鎖等各種類型,proc文件系統中的/proc/softirqs可以觀察軟中斷的運行情況。
在Linux中,每個CPU都對應一個軟中斷內核線程,名字是ksoftirqd/CPU編號。當軟中斷事件頻率過高時,內核線程會因爲CPU 使用率過高而導致軟中斷處理不及時,進而引發網絡收發延遲、調度緩慢等性能問題。軟中斷CPU使用率過高也是一種最常見的性能問題。




2、中斷查看

proc文件系統是內核空間和用戶空間進行通信的機制,可以用來查看內核的數據結構或者動態修改內核配置。
/proc/softirqs提供了軟中斷的運行情況。
/proc/interrupts提供了硬中斷的運行情況。
Linux性能優化(十一)——CPU性能優化原理
第一列內容是軟中斷類型,軟中斷包括10個類別,分別對應不同的工作類型。NET_RX表示網絡接收中斷,NET_TX表示網絡發送中斷。通常同一種中斷在不同CPU上的累積次數是同一個數量級,相差不大。
/proc/softirqs內容是系統運行以來累積中斷次數,生產中通常關注中斷次數的變化速率,因此使用watch -d cat /proc/softirqs查看軟中斷變化情況。
Linux性能優化(十一)——CPU性能優化原理
TIMER(定時中斷)、NET_RX(網絡接收)、SCHED(內核調度)、RCU(RCU鎖)等軟中斷都在不停變化。NET_RX、NET_TX軟中斷類型需要使用sar工具查看網絡數據收發。
Linux性能優化(十一)——CPU性能優化原理
第一列是表示報告的時間。
第二列IFACE表示網卡。
第三列rxpck/s表示每秒接收的網絡幀數量,PPS。
第四列txpck/s表示每秒發送的網絡幀數,PPS。
第五列rxkB/s表示每秒接收的千字節數,BPS。
第六列txkB/s表示每秒發送的千字節數,BPS。













3、不可中斷進程

不可中斷狀態表示進程正在跟硬件交互,爲了保護進程數據和硬件的一致性,系統不允許其它進程或中斷打斷處於不可中斷狀態的進程。進程長時間處於不可中斷狀態,通常表示系統存在IO性能問題。

4、殭屍進程

殭屍進程表示進程已經退出,但其父進程還沒有回收子進程佔用的資源。短暫的殭屍狀態通常不必理會,但進程長時間處於殭屍狀態表示可能有應用程序沒有正常處理子進程退出。

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