聊聊 Linux 中 CPU 上下文切換
-
什麼是 CPU 上下文
-
CPU 上下文切換
-
上一任務的 CPU 上下文保存在哪?
-
進程上下文切換
-
內核空間和用戶空間
-
top 命令查看 CPU 資源
-
系統調用
-
進程上下文切換 和 系統調用的區別?
-
進程切換的常見場景
-
線程上下文切換
-
中斷上下文切換
-
上下文切換的消耗
-
補充:vmstat 命令查看整體 CPU 上下文切換情況
-
補充:pidstat 命令查看進程的 CPU 上下文切換情況
什麼是 CPU 上下文
Linux 是一個多任務的操作系統,多任務操作系統是指多個進程運行在一個 CPU 中互不打擾,看起來像同時運行一樣。多任務的操作系統這樣就可以支持遠大於 CPU 數量的任務 "同時運行"。但是我們都知道這其實是一個錯覺,真正是系統在很短的時間內將 CPU 輪流執行各個任務,給用戶造成多任務 "同時運行" 的感覺。
其中有一個問題,在每次執行任務之前,CPU 必須要知道從哪裏加載任務,以及加載後從哪裏開始運行,操作系統通過CPU中寄存器和程序計數器配合,來保存和恢復相應進度的信息
CPU 寄存器:CPU 寄存器是 CPU 內置的速度極快的內存;程序計數器:程序計數器會存儲 CPU 正在執行的指令位置,或者即將執行的指令位置。
在任務調度的過程中, 而這些信息都保存在 CPU 的寄存器中,其中即將執行的下一條指令的地址被保存在程序計數器上。我們將這些信息稱爲 CPU 的上下文,也叫硬件上下文。
當某一進程自願放棄它的 CPU 時間或者系統分配的時間片用完時,就會發生 CPU 上下文切換。
CPU 上下文切換
操作系統 OS 在切換運行任務時,將上一任務的上下文保存起來,然後加載新任務的上下文到CPU寄存器,最後再跳轉到程序計數器所指的新位置上執行新任務的這一動作,被稱爲 CPU 上下文切換。
CPU 上下文切換的步驟:
-
將前一個 CPU 的上下文(也就是 CPU 寄存器和程序計數器裏邊的內容)保存起來;
-
然後加載新任務的上下文到寄存器和程序計數器;
-
最後跳轉到程序計數器所指的新位置,運行新任務。
-
被保存起來的上下文會存儲到系統內核中,等待任務重新調度執行時再次加載進來。
CPU 的上下文切換分三種:進程上下文切換、線程上下文切換、中斷上下文切換。
上一任務的 CPU 上下文保存在哪?
我們知道因爲 CPU 過於昂貴,其性能與其他儲存設備有數量級的差距,爲了充分壓榨其性能,計算機將 CPU 的時間進行分片,讓各個程序在 CPU 上輪轉執行,被剝奪執行權的程序,等後面 CPU 繼續執行它的時候,這時需要一個數據結構來保存相關信息,以便之後恢復繼續執行,這個其實就是進程。
CPU 上下文會被保存在進程的內核空間(kernel space)上。OS 在給每個進程分配虛擬內存空間時,會分配一個內核空間,這部分內存只能由內核代碼訪問。OS 在切換 CPU 上下文前,會先將當前 CPU 的通用寄存器、PC 等進程現場信息保存在進程的內核空間上,待下次切換時,再取出重新裝載到 CPU 上,以恢復任務的運行。
進程上下文切換
內核空間和用戶空間
我們知道爲了限制不同的指令的訪問能力,提升安全,Linux 按照特權等級,把進程的運行空間分爲內核空間和用戶空間 。進程既可以在用戶空間運行,又可以在內核空間中運行。進程在用戶空間運行時,被稱爲進程的用戶態,而陷入內核空間的時候,被稱爲進程的內核態。
- 內核空間 (Ring 0):具有最高權限,可以直接訪問所有資源(讀取文件)
常見的內核操作:分配內存、IO 操作、創建子進程……
- 用戶空間 (Ring 3):只能訪問受限資源,不能直接訪問內存等硬件設備,必須通過系統調用進入到內核中,才能訪問這些特權資源
常見的用戶態空間程序:數據庫、web 服務器、shell 腳本、Java 程序或者其他常見語言的程序……
我們一起看下 Linux 整體架構圖:
top 命令查看 CPU 資源
在 linux 系統使用top命令查看 cpu 時,能看到用戶態和內核態佔用的 cpu 資源
其中各項數據表示內容:
系統調用
對於一個進程來說,比如 web 服務的進程,一般是運行在用戶態的,但是當需要訪問內存、磁盤等硬件設備的時候需要先進入到內核態中,也就是從用戶態到內核態的轉變,而這種轉變需要藉助系統調用來實現。系統調用是內核向用戶進程提供服務的唯一方法。
比如查看文件時,需要執行多次系統調用:open() 打開文件,read() 讀取文件內容,write() 將文件內容輸出到控制檯,最後 close() 關閉文件等。系統調用的過程如下:
-
把 CPU 寄存器裏原來用戶態的指令位置保存起來;
-
爲了執行內核代碼,CPU 寄存器需要更新爲內核態指令的新位置,最後跳轉到內核態運行內核任務;
-
系統調用結束後,CPU 寄存器需要恢復原來保存的用戶態,然後再切換到用戶空間,繼續運行進程;
我們可以發現一次系統調用的過程,其實是發生了兩次 CPU 上下文切換(用戶態 - 內核態 - 用戶態)。
需要注意的是: 系統調用過程中,不涉及虛擬內存等進程用戶態的資源,也不會切換進程,也就是系統調用過程中一直是同一個進程在運行。系統調用過程也通常稱爲特權模式切換。
進程上下文切換 和 系統調用的區別?
- 進程上下文切換是指,從一個進程切換到另一個進程;系統調用過程一直是同一個進程在運行,屬於進程之內的上下文切換
需要注意的是:進程是由內核來管理和調度的,進程的切換隻能發生在內核態, 保存上下文和恢復上下文的過程並不免費,需要消耗一定資源
-
進程的上下文不僅包括了虛擬內存、棧、全局變量等用戶空間的資源,還包括了內核堆棧、寄存器等內核空間的狀態。而系統調用這裏沒有涉及到虛擬內存等這些進程用戶態的資源
-
因此進程的上下文切換就比系統調用時多了一步:在保存當前進程的內核狀態和 CPU 寄存器之前,需要先把該進程的虛擬內存、棧等保存下來;而加載了下一進程的內核態後,還需要刷新進程的虛擬內存和用戶棧。
進程切換的常見場景
進程切換時需要切換上下文,換句話說,只有在進程調度的時候,才需要切換上下文。Linux 爲每個 CPU 都維護了一個就緒隊列,將活躍進程(即正在運行和正在等待 CPU 的進程)按照優先級和等待 CPU 的時間排序,然後選擇最需要 CPU 的進程,也就是優先級最高和等待 CPU 時間最長的進程來運行。進程切換的場景有:
-
進程時間片耗盡,爲了保證所有進程可以得到公平調度,CPU 時間被劃分爲一段段的時間片,這些時間片再被輪流分配給各個進程。當某個進程的時間片耗盡了,就會被系統掛起,切換到其它正在等待 CPU 的進程運行。
-
進程在系統資源不足(比如內存不足)時,要等到資源滿足後纔可以運行,這個時候進程也會被掛起,並由系統調度其他進程運行。
-
進程通過睡眠函數 sleep 主動把自己掛起,CPU 會重新調度;
-
當有 CPU 發現優先級更高的進程運行時,爲了去運行高優先級進程,當前進程會被掛起;
-
發生硬中斷,CPU 上的進程會被掛起,然後去執行內核中的中斷服務進程。
線程上下文切換
對操作系統來說,進程是資源分配的基本單位,而線程則是任務調度的基本單位。 內核中的任務調度實際是在調度線程,進程只是給線程提供虛擬內存、全局變量等資源。線程上下文切換時,共享相同的虛擬內存和全局變量等資源不需要修改。而線程自己的私有數據,如棧和寄存器等,上下文切換時需要保存。
關於進程和線程的區別:
-
當進程中只有一個線程時,可以認爲進程就等於線程。
-
當進程擁有多個線程時,這些線程會共享父進程的資源(即共享相同的虛擬內存和全局變量等資源)。這些資源在上下文切換時是不需要修改的。
-
另外,線程也有自己的私有數據,比如棧和寄存器等,這些在上下文切換時也是需要保存的。
因此線程上下文切換有兩種情況:
-
前後兩個線程屬於不同進程,因爲資源不共享,所以切換過程就跟進程上下文切換是一樣的;
-
前後兩個線程屬於同一個進程,因爲虛擬內存是共享的,所以在切換時,虛擬內存這些資源就保持不動,只需要切換線程的私有數據、寄存器等不共享的數據。
中斷上下文切換
上下文切換有時也因硬件中斷而觸發。硬件中斷是指硬件設備(如鍵盤、鼠標、調試解調器、系統時鐘)給內核發送的一個信號,該信號表示一個事件(如按鍵、鼠標移動、從網絡連接接收到數據)發生了。
爲了快速響應硬件的事件,中斷處理會打斷進程的正常調度和執行,然後調用中斷處理程序,響應設備事件。在打斷其他進程時,需要先將進程當前的狀態保存下來,等中斷結束後,進程仍然可以恢復回來。
跟進程上下文不同,中斷上下文切換不涉及進程的用戶態。所以,即便中斷過程打斷了一個正處在用戶態的進程,也不需要保存和恢復這個進程的虛擬內存、全局變量等用戶態資源。中斷上下文,只包括內核態中斷服務程序執行所必需的狀態,也就是 CPU 寄存器、內核堆棧、硬件中斷參數等。
中斷上下文切換並不涉及到進程的用戶態。所以即便中斷過程打斷了一個正處在用戶態的進程,也不需要保存和恢復這個進程的虛擬內存、全局變量等用戶態資源。中斷上下文,其實只包括內核態中斷服務程序執行所必須的狀態,包括 CPU 寄存器、內核堆棧、硬件中斷參數等。
對同一個 CPU 來說,中斷處理比進程擁有更高的優先級,所以中斷上下文切換不會與進程上下文切換同時發生。並且,由於中斷會打斷正常進程的調度和執行,所以大部分中斷處理程序都短小精悍,以便可以儘快完成。
上下文切換的消耗
上下文切換通常是計算密集型的。也就是說,它需要相當可觀的處理器時間,在每秒幾十上百次的切換中,每次切換都需要納秒量級的時間。所以,上下文切換對系統來說意味着消耗大量的 CPU 時間,事實上,可能是操作系統中時間消耗最大的操作。
Linux 相比與其他操作系統(包括其他類 Unix 系統)有很多的優點,其中有一項就是,其上下文切換和模式切換的時間消耗非常少。
根據 Tsuna 的測試報告,每次上下文切換都需要幾十納秒到數微妙的 CPU 時間,這個時間還是相當可觀的。不管是哪種場景導致的上下文切換,你都應該知道:
-
CPU 上下文切換,是保證 Linux 系統正常工作的核心功能,一般情況下不需要開發人員特別關注。
-
但過多的上下文切換,會把 CPU 時間消耗在寄存器、內核棧以及虛擬內存等數據的保存和恢復上,從而縮短進程真正運行的時間,耗費大量的 CPU,甚至嚴重降低系統的整體性能。
補充:vmstat 命令查看整體 CPU 上下文切換情況
上面已經介紹到 CPU 上下文切換分爲進程上下文切換、線程上下文切換、中斷上下文切換,那麼過多的上下文切換會把 CPU 的時間消耗在寄存器、內核棧以及虛擬內存等數據的保存和恢復上,縮短進程真正運行的時間,成爲系統性能大幅下降的一個因素
所以我們可以使用 vmstat 這個工具來查詢系統的上下文切換情況,vmstat 是一個常用的系統性能分析工具,可以用來分析 CPU 上下文切換和中斷的次數 執行如下的命令:vmstat 5 (每隔 5s 輸出一組數據)
procs:procs 中有 r 和 b 列,它報告進程統計信息。在上面的輸出中,在運行隊列(r)中有兩個進程在等待 CPU 並有零個休眠進程(b)。通常,它不應該超過處理器(或核心)的數量,如果你發現異常,最好使用 top 命令進一步地排除故障。
-
r:等待運行的進程數。
-
b:休眠狀態下的進程數。
memory:memory 下有報告內存統計的 swpd、free、buff 和 cache 列。你可以用 free -m 命令看到同樣的信息。在上面的內存統計中,統計數據以千字節表示,這有點難以理解,最好添加 M 參數來看到以兆字節爲單位的統計數據。
-
swpd:使用的虛擬內存量。
-
free:空閒內存量。
-
buff:用作緩衝區的內存量。
-
cache:用作高速緩存的內存量。
-
inact:非活動內存的數量。
-
active:活動內存量。
swap:swap 有 si 和 so 列,用於報告交換內存統計信息。你可以用 free -m 命令看到相同的信息。
-
si:從磁盤交換的內存量(換入,從 swap 移到實際內存的內存)。
-
so:交換到磁盤的內存量(換出,從實際內存移動到 swap 的內存)。
I/O:I/O 有 bi 和 bo 列,它以 “塊讀取” 和“塊寫入”的單位來報告每秒磁盤讀取和寫入的塊的統計信息。如果你發現有巨大的 I/O 讀寫,最好使用 iotop 和 iostat 命令來查看。
-
bi:從塊設備接收的塊數。
-
bo:發送到塊設備的塊數。
system:system 有 in 和 cs 列,它報告每秒的系統操作。
-
in:每秒的系統中斷數,包括時鐘中斷。
-
cs:系統爲了處理所以任務而上下文切換的數量。
CPU:CPU 有 us、sy、id 和 wa 列,報告(所用的) CPU 資源佔總 CPU 時間的百分比。如果你發現異常,最好使用 top 和 free 命令。
-
us:處理器在非內核程序消耗的時間。
-
sy:處理器在內核相關任務上消耗的時間。
-
id:處理器的空閒時間。
-
wa:處理器在等待 IO 操作完成以繼續處理任務上的時間。
補充:pidstat 命令查看進程的 CPU 上下文切換情況
執行如下的命令:pidstat,查看進程的 CPU 上下文切換情況 如果沒有安裝,yum install sysstat安裝即可
在結果中你能看到如下內容:
-
PID - 被監控的任務的進程號
-
%usr - 當在用戶層執行(應用程序)時這個任務的 cpu 使用率,和 nice 優先級無關。注意這個字段計算的 cpu 時間不包括在虛擬處理器中花去的時間。
-
%system - 這個任務在系統層使用時的 cpu 使用率。
-
%guest - 任務花費在虛擬機上的 cpu 使用率(運行在虛擬處理器)。
-
%CPU - 任務總的 cpu 使用率。在 SMP 環境(多處理器)中,如果在命令行中輸入 - I 參數的話,cpu 使用率會除以你的 cpu 數量。
-
CPU - 正在運行這個任務的處理器編號。
-
Command - 這個任務的命令名稱。
參考資料:
《Linux 內核設計與實現》
《Linux 性能優化實戰》
http://ifeve.com/context-switch-definition https://www.it610.com/article/1289356670568308736.htm
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/l7UIfQVkQoRmRgc_ufkOCA