CPU上下文切換(上)

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安裝特權等級,把進程的運行空間分爲內核空間和用戶空間

![] 企業微信截圖_15486532103731.png

  • 內核空間(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寄存器、內核堆棧、硬件中斷參數等。

​ 對同一個cup來說,中斷處理比進程擁有更高的優先級,所以中斷上下文切換並不會與進程上線文切換同時發生。所以大部分中斷處理程序都短小精悍,以便儘快的執行結束。

總結

  1. cpu上下文切換,時保持Linux系統正常運行的核心功能之一,一般情況不需要我們特別關注。
  2. 過多的上下文切換,會把cpu時間消耗在寄存器、內核棧以及虛擬內存等數據的保存和恢復上,從而縮短了進程真正運行的時間,導致系統性能下降。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章