CPU上下文切換
我們經常說的平均負載和cpu升高沒有直接的關系,在不同的場景cpu升高會導緻系統負載,但是系統負載不一定是cpu升高導緻的。
一、系統負載過高的三種場景
- cpu密集型程序,使用大量cpu會導緻平均負載升高,此時這兩者是一緻的。
- io密集型程序,等待io也會導緻平均負載升高,但cpu不一定很高。
- 大量等待cpu的程序排程也會導緻平均負載升高,此時cpu使用率也會比較高。
大量程序競争cpu(也就是上面的第三個場景),往往是被忽略的,cpu雖然沒有使用,隻是在競争,也會發生負載嗎?
我們都知道linux是一個多任務作業系統,它支援遠大于cpu數量的任務同時運作,當然這些任務不是同時運作,而是系統在很短時間内,将cpu輪流配置設定給它們,造成多任務同時運作的錯覺。而每個任務運作前,cpu需要知道任務從哪裡加載、又從哪裡開始運作,也就是說,需要系統事先幫它設定好cpu寄存器和程式計數器。
二、CPU寄存器和程式計數器
1. cpu寄存器:
cpu内置的容量小、但速度極快的記憶體。
2. 程式計數器:
是用來存儲cpu正在執行的指令位置、或者即将執行的下一條指令位置。他們都是cpu在運作任何任務前,必須的依賴環境,是以也被叫做cpu上下文。
3. cpu上下文切換
cpu上下文切換:就是先前一個任務的cpu上下文(也就是CPU寄存器和程式計數器)儲存起來,然後加載新任務的上下文到這些寄存器和程式計數器,最後再跳轉到程式計數器所指的新位置,運作新任務。
而這些儲存下來的上下文,會存儲在系統核心中,并在任務重新排程執行時再次加載進來。這樣就保證原來任務的狀态不受影響,讓任務看起來還是連續運作。
根據任務不同,cpu的上下文切換就可以分為不同的幾個場景,程序上下文切換,線程上下文切換以及中斷上下文切換。
3.1. 程序上下文切換
linux安裝特權等級,把程序的運作空間分為核心空間和使用者空間
![]

- 核心空間(Ring0)具有最高權限,可以直接通路所有資源;
- 使用者空間(Ring3)隻能通路受限權限,不能直接通路記憶體等硬體裝置,必須通過系統調用陷入到核心中,才能通路特權資源。
是以,程序既可以在使用者空間運作又可以在核心空間運作,在使用者空間運作時,稱為使用者态。在核心空間運作時,稱為核心态。
3.1.1. 系統調用是否發生cpu上下文切換
- 系統調用:cpu寄存器裡面原來的使用者态的指令位置,需要先儲存起來。接着,為了執行核心态代碼,cpu寄存器需要更新為核心态指令的新位置。最後,才跳轉到核心态運作核心任務。
- 系統調用結束後:cpu寄存器需要恢複原來儲存的使用者态,然後再切換到使用者空間,繼續運作程序。是以,一次系統調用,其實發送了兩次cpu上下文切換。
- 系統調用過程中,并不會涉及到虛拟記憶體等程序使用者态的資源,也不會切換程序。是以,系統調用通常稱為特權模式切換,但實際上,系統調用也無法避免cpu上下文切換。
3.1.2. 程序上下文切換跟系統調用差別
程序是由核心來管理和排程的,程序的切換隻能發生在核心态,是以,程序的上下文不僅包括了使用者态的虛拟記憶體,使用者棧,全局變量等資源。還包括了核心棧、寄存器等核心空間的狀态。
是以,程序的切換比系統調用多了一步:在儲存目前程序的核心狀态和cpu寄存器之前,需要先把使用者态的虛拟記憶體,使用者棧儲存下來;在加載了下一程序的核心态後,需要重新整理程序的虛拟核心和使用者棧。
儲存上下文和恢複上下文的程序并不是免費的,需要記憶體在cpu上運作才能完成。
每次上下文切換都需要幾十納秒到微妙的cpu時間。這個時間還是相當可觀。在程序上下文切換次數較多的情況下,很容易導緻cpu将大量時間耗費在寄存器、核心棧以及虛拟記憶體等資源的儲存和恢複上,進而大大縮短了真正運作程序的時間。這也是導緻平均負載升高的重要因素。
linux通過(tlb)來管理虛拟記憶體和實體記憶體直接的映射關系,當虛拟記憶體更新後,tlb也要更新,記憶體通路随之變慢。特别是在多處理器系統上,緩存被多個處理器共享,重新整理緩存不僅影響目前處理器的程序,還會影響共享緩存的其他處理器的程序。
3.1.3. 程序什麼時候切換
程序執行完終止了,會釋放cpu,這個時候cpu從就緒隊列裡面拿一個新的程序運作,還有其他的一些非正常場景也會導緻程序切換。
- 時間片耗盡:為了保證是以程序可以得到公平排程,CPU時間被劃分成一段段的時間片,這些時間片輪流配置設定給各個程序。當某個時間片耗盡了,就會被系統挂起,切換到其他等待cpu的程序運作。
- 系統資源不足(如:記憶體不足):這個時候程序會被挂起,并由系統排程其他程序運作。
- 程序主動挂起: 當程序通過睡眠函數sleep這樣的方式将自己主動挂起時。
- 高優先級程序:當有優先級高的程序出現時,為了保證優先級高的程序運作,目前程序會被系統挂起
- 硬體中斷時:當發生硬體中斷時,Cpu上的程序會被中斷挂起,轉而執行核心中的中斷服務程式。
3.2. 線程上下文切換
線程和程序最大的差別在于,線程是排程的基本機關,而程序則是資源擁有的基本機關。所謂核心中的任務排程,實際上的排程對象是線程;而程序隻是給線程提供了虛拟記憶體、全局變量等資源。是以,對于線程和程序可以了解為: - 當程序隻有一個線程時,可以任務程序就是線程。
- 當程序擁有多個線程時,這些線程共享相同的虛拟記憶體、全局變量等資源。這些資源在上下文切換時是不需要修改的。
- 另外,線程也有自己的私有資料,如:棧和寄存器等,這些在上下文切換時也是需要儲存的。
是以說:
當兩個線程不屬于同一程序時,線程上下文切換時。因為資源不共享,是以切換就相當于時程序間切換。
當兩個線程屬于同一程序時,線程上下文切換時。因為資源共享,隻需要切換線程私有資料。
程序間切換消耗的資源多于線程間切換,是以就出現了多線程代替多程序的優勢。
3.3. 中斷上下文切換
為了快速響應硬體的事件,中斷處理會打斷程序的正常排程和執行,轉而調用中斷處理程式,響應裝置事件。在打斷其他程序時,就需要将程序目前狀态儲存起來。
- cpu上下文切換,時保持Linux系統正常運作的核心功能之一,一般情況不需要我們特别關注。
- 過多的上下文切換,會把cpu時間消耗在寄存器、核心棧以及虛拟記憶體等資料的儲存和恢複上,進而縮短了程序真正運作的時間,導緻系統性能下降。