轉載自:https://blog.csdn.net/czd3355/article/details/85118727
我們在介紹什么是CPU上下文切換之前,我們先介紹一下Linux,Linux 是一個多任務操作系統,它支持遠大於 CPU 數量的任務(這里可以直接理解為進程)同時運行。當然,這些任務實際上並不是真的在同時運行,而是因為系統在很短的時間內,將 CPU 輪流分配給它們,造成多任務同時運行的錯覺。而在每個任務運行前,CPU 都需要知道任務從哪里加載、又從哪里開始運行(這里任務的運行,就是進程的運行,簡單的理解電腦運行一個進程,其實就是解釋執行一段源碼,而CPU就是真正執行源碼的,源碼放在內存中的哪一塊,這個CPU是要知道的),也就是說,需要系統事先幫它設置好CPU 寄存器和程序計數器。
1.什么是進程
上面我們講到進程,那么到底什么是進程呢?進程(有時候也稱做任務)是指一個程序運行的實例。在 Linux 系統中,線程就是能並行運行並且與他們的父進程(創建他們的進程)共享同一地址空間(一段內存區域)和其他資源的輕量級的進程。
2.什么是 CPU 上下文
CPU 寄存器和程序計數器就是 CPU 上下文,因為它們都是 CPU 在運行任何任務前,必須的依賴環境。上下文
指某一時間點 CPU 寄存器和程序計數器的內容
CPU 寄存器是 CPU 內置的容量小、但速度極快的內存(與之對應的是 CPU 外部相對較慢的 RAM 主內存)。
程序計數器則是一個專用的寄存器,是用來存儲 CPU 正在執行的指令位置、或者即將執行的下一條指令位置。
3.什么是 CPU 上下文切換
CPU上下文切換就是保存上一個任務運行的寄存器和計數器信息切換到加載下一個任務的寄存器和計數器的過程
就是先把前一個任務的 CPU 上下文(也就是 CPU 寄存器和程序計數器)保存起來,然后加載新任務的上下文到這些寄存器和程序計數器,最后再跳轉到程序計數器所指的新位置,運行新任務。
而這些保存下來的上下文,會存儲在系統內核中,並在任務重新調度執行時再次加載進來。這樣就能保證任務原來的狀態不受影響,讓任務看起來還是連續運行。
詳細解釋就是:上下文切換可以認為是內核(操作系統的核心)在 CPU 上對於進程(包括線程)進行以下的活動:(1)掛起一個進程,將這個進程在 CPU 中的狀態(上下文)存儲於內存中的某處,(2)在內存中檢索下一個進程的上下文並將其在 CPU 的寄存器中恢復,(3)跳轉到程序計數器所指向的位置(即跳轉到進程被中斷時的代碼行),以恢復該進程。
4.CPU 上下文切換的類型
1.進程上下文切換
Linux 按照特權等級,把進程的運行空間分為內核空間和用戶空間,分別對應着下圖中, CPU 特權等級的 Ring 0 和 Ring 3。
內核空間(Ring 0)具有最高權限,可以直接訪問所有資源;
用戶空間(Ring 3)只能訪問受限資源,不能直接訪問內存等硬件設備,必須通過系統調用陷入到內核中,才能訪問這些特權資源。
進程既可以在用戶空間運行,又可以在內核空間中運行。進程在用戶空間運行時,被稱為進程的用戶態,而陷入內核空間的時候,被稱為進程的內核態。
2.系統調用
從用戶態到內核態的轉變,需要通過系統調用來完成。比如,當我們查看文件內容時,就需要多次系統調用來完成:首先調用 open() 打開文件,然后調用 read() 讀取文件內容,並調用 write() 將內容寫到標准輸出,最后再調用 close() 關閉文件。
在這個過程中就發生了 CPU 上下文切換,整個過程是這樣的:
1、保存 CPU 寄存器里原來用戶態的指令位
2、為了執行內核態代碼,CPU 寄存器需要更新為內核態指令的新位置。
3、跳轉到內核態運行內核任務。
4、當系統調用結束后,CPU 寄存器需要恢復原來保存的用戶態,然后再切換到用戶空間,繼續運行進程。
所以,一次系統調用的過程,其實是發生了兩次 CPU 上下文切換。(用戶態-內核態-用戶態)
不過,需要注意的是,系統調用過程中,並不會涉及到虛擬內存等進程用戶態的資源,也不會切換進程。這跟我們通常所說的進程上下文切換是不一樣的:進程上下文切換,是指從一個進程切換到另一個進程運行;而系統調用過程中一直是同一個進程在運行。
所以,系統調用過程通常稱為特權模式切換,而不是上下文切換。系統調用屬於同進程內的 CPU 上下文切換。但實際上,系統調用過程中,CPU 的上下文切換還是無法避免的。
3.系統調用和進程上下文切換的區別
- 系統調用都是在同一個進程中發生
- 進程上下文切換是從一個進程切換到第二個進程中運行
- 進程上下文切換除了需要保存 虛擬內存,棧,全局變量 等用戶空間的資源,還包括 內核堆棧,寄存器等內核空間的狀態
4.發生進程上下文切換的場景
- 為了保證所有進程可以得到公平調度,CPU 時間被划分為一段段的時間片,這些時間片再被輪流分配給各個進程。這樣,當某個進程的時間片耗盡了,就會被系統掛起,切換到其它正在等待 CPU 的進程運行。
- 進程在系統資源不足(比如內存不足)時,要等到資源滿足后才可以運行,這個時候進程也會被掛起,並由系統調度其他進程運行。
- 當進程通過睡眠函數 sleep 這樣的方法將自己主動掛起時,自然也會重新調度。
- 當有優先級更高的進程運行時,為了保證高優先級進程的運行,當前進程會被掛起,由高優先級進程來運行
- 發生硬件中斷時,CPU 上的進程會被中斷掛起,轉而執行內核中的中斷服務程序。
5.線程上下文切換
1:線程與進程的區別: 線程是調度的基本單位,進程是資源擁有的基本單位 通俗理解:系統內核中的任務調度實際上調度的對象是線程,而進程只是給線程提供了虛擬內存,全局變量等資源這些資源在線程上下文切換時是不需要修改的
2:結論:如果是同一個進程之間的線程上下文切換時,進程提供的全局資源不需要單獨保存;如果是不同進程之前的線程上下文切換相對前一種會消耗資源是cpu更多
6.發生線程上下文切換的場景
- 前后兩個線程屬於不同進程。此時,因為資源不共享,所以切換過程就跟進程上下文切換是一樣。
- 前后兩個線程屬於同一個進程。此時,因為虛擬內存是共享的,所以在切換時,虛擬內存這些資源就保持不動,只需要切換線程的私有數據、寄存器等不共享的數據
7.中斷上下文切換
為了快速響應硬件的事件,中斷處理會打斷進程的正常調度和執行,轉而調用中斷處理程序,響應設備事件。而在打斷其他進程時,就需要將進程當前的狀態保存下來,這樣在中斷結束后,進程仍然可以從原來的狀態恢復運行。
跟進程上下文不同,中斷上下文切換並不涉及到進程的用戶態。所以,即便中斷過程打斷了一個正處在用戶態的進程,也不需要保存和恢復這個進程的虛擬內存、全局變量等用戶態資源。中斷上下文,其實只包括內核態中斷服務程序執行所必需的狀態,包括 CPU 寄存器、內核堆棧、硬件中斷參數等。
對同一個 CPU 來說,中斷處理比進程擁有更高的優先級,所以中斷上下文切換並不會與進程上下文切換同時發生。同樣道理,由於中斷會打斷正常進程的調度和執行,所以大部分中斷處理程序都短小精悍,以便盡可能快的執行結束。
8.為什么要監控上下文切換次數
從上面我們解釋了什么是CPU的上下文切換,那么這個時候會有一個問題,為什么我們做監控的時候,需要監控CPU的上下文切換次數呢?說到這里我還要解釋兩個概念,自願上下文切換和非自願上下文切換。
自願上下文切換:是指進程無法獲取所需資源,導致的上下文切換。比如說, I/O、內存等系統資源不足時,就會發生自願上下文切換。
非自願上下文切換:則是指進程由於時間片已到等原因,被系統強制調度,進而發生的上下文切換。比如說,大量進程都在爭搶 CPU 時,就容易發生非自願上下文切換。
上次文切換次數主要監控哪些方面:自願上下文切換次數,非自願上下文切換次數,中斷上下文切換。
每秒上下文切換多少次算正常?
這個數值主要取決於系統CPU的性能,如果上下文切換比較穩定,那在1萬以下都算是正常,如果超過1萬或者切換次數出現很大的增長,就很可能出現了性能問題。 cswch ,自願上下文切換的次數增多了,說明系統正在等待資源,有可能發生了I/O等其它問題; nvcswch ,非自願上下文切換的次數增多了,說明進程都在強制調度,也就是在爭搶CPU,說明CPU性能成了瓶頸; in,中斷次數增多了,說明CPU被中斷,通過分析/proc/interrupt文件來確認中斷類型。