聊聊 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