聊聊Linux中CPU上下文切換

来源:https://www.cnblogs.com/xiaoniuhululu/archive/2022/10/08/16768062.html
-Advertisement-
Play Games

作者:小牛呼嚕嚕 | https://xiaoniuhululu.com 電腦內功、JAVA底層、面試相關資料等更多精彩文章在公眾號「小牛呼嚕嚕 」 什麼是CPU上下文 Linux是一個多任務的操作系統,多任務操作系統是指多個進程運行在一個 CPU 中互不打擾,看起來像同時運行一樣。多任務的操作系 ...


目錄

作者:小牛呼嚕嚕 | https://xiaoniuhululu.com
電腦內功、JAVA底層、面試相關資料等更多精彩文章在公眾號「小牛呼嚕嚕 」

什麼是CPU上下文

Linux是一個多任務的操作系統,多任務操作系統是指多個進程運行在一個 CPU 中互不打擾,看起來像同時運行一樣。多任務的操作系統這樣就可以支持遠大於CPU數量的任務"同時運行"。但是我們都知道這其實是一個錯覺,真正是系統在很短的時間內將CPU輪流執行各個任務,給用戶造成多任務"同時運行"的感覺。
其中有一個問題,在每次執行任務之前,CPU必須要知道從哪裡載入任務,以及載入後從哪裡開始運行,操作系統通過CPU中寄存器和程式計數器配合,來保存和恢復相應進度的信息

CPU 寄存器:CPU 寄存器是 CPU 內置的速度極快的記憶體;
程式計數器:程式計數器會存儲 CPU 正在執行的指令位置,或者即將執行的指令位置。

在任務調度的過程中, 而這些信息都保存在CPU的寄存器中,其中即將執行的下一條指令的地址被保存在程式計數器上。我們將這些信息稱為CPU的上下文,也叫硬體上下文
當某一進程自願放棄它的 CPU 時間或者系統分配的時間片用完時,就會發生CPU上下文切換。

CPU上下文切換

操作系統OS在切換運行任務時,將上一任務的上下文保存起來,然後載入新任務的上下文到CPU寄存器,最後再跳轉到程式計數器所指的新位置上執行新任務的這一動作,被稱為CPU上下文切換

CPU上下文切換的步驟:

  1. 將前一個 CPU 的上下文(也就是 CPU 寄存器和程式計數器裡邊的內容)保存起來;
  2. 然後載入新任務的上下文到寄存器和程式計數器;
  3. 最後跳轉到程式計數器所指的新位置,運行新任務。
  4. 被保存起來的上下文會存儲到系統內核中,等待任務重新調度執行時再次載入進來。

CPU 的上下文切換分三種:進程上下文切換、線程上下文切換、中斷上下文切換

上一任務的CPU上下文保存在哪?

我們知道因為CPU過於昂貴,其性能與其他儲存設備有數量級的差距,為了充分壓榨其性能,電腦將CPU的時間進行分片,讓各個程式在CPU上輪轉執行,被剝奪執行權的程式,等後面CPU繼續執行它的時候,這時需要一個數據結構來保存相關信息,以便之後恢復繼續執行,這個其實就是進程。
CPU上下文會被保存在進程的內核空間(kernel space)上。OS在給每個進程分配虛擬記憶體空間時,會分配一個內核空間,這部分記憶體只能由內核代碼訪問。OS在切換CPU上下文前,會先將當前CPU的通用寄存器、PC等進程現場信息保存在進程的內核空間上,待下次切換時,再取出重新裝載到CPU上,以恢復任務的運行。

進程上下文切換

內核空間和用戶空間

我們知道為了限制不同的指令的訪問能力,提升安全,Linux 按照特權等級,把進程的運行空間分為內核空間用戶空間 。進程既可以在用戶空間運行,又可以在內核空間中運行。進程在用戶空間運行時,被稱為進程的用戶態,而陷入內核空間的時候,被稱為進程的內核態

  1. 內核空間(Ring 0):具有最高許可權,可以直接訪問所有資源(讀取文件)

常見的內核操作:分配記憶體、IO操作、創建子進程……

  1. 用戶空間(Ring 3):只能訪問受限資源,不能直接訪問記憶體等硬體設備,必須通過系統調用進入到內核中,才能訪問這些特權資源

常見的用戶態空間程式:資料庫、web伺服器、shell腳本、Java程式或者其他常見語言的程式……

我們一起看下Linux整體架構圖:

top命令查看CPU資源

在linux系統使用top命令查看cpu時,能看到用戶態和內核態占用的cpu資源

其中各項數據表示內容:

us 用戶空間占用CPU百分比
sy 內核空間占用CPU百分比
ni 用戶進程空間內改變過優先順序的進程占用CPU百分比
id 空閑CPU百分比
wa 等待輸入輸出的CPU時間百分比
hi 硬體中斷
si 軟體中斷
st 實時

系統調用

對於一個進程來說,比如web服務的進程,一般是運行在用戶態的,但是當需要訪問記憶體、磁碟等硬體設備的時候需要先進入到內核態中,也就是從用戶態到內核態的轉變,而這種轉變需要藉助系統調用來實現。系統調用是內核向用戶進程提供服務的唯一方法。
比如查看文件時,需要執行多次系統調用:open()打開文件,read()讀取文件內容,write()將文件內容輸出到控制台,最後close()關閉文件等。
系統調用的過程如下:

  1. 把 CPU 寄存器里原來用戶態的指令位置保存起來;
  2. 為了執行內核代碼,CPU 寄存器需要更新為內核態指令的新位置,最後跳轉到內核態運行內核任務;
  3. 系統調用結束後,CPU 寄存器需要恢複原來保存的用戶態,然後再切換到用戶空間,繼續運行進程;

我們可以發現一次系統調用的過程,其實是發生了兩次 CPU 上下文切換(用戶態-內核態-用戶態)。
需要註意的是:系統調用過程中,不涉及虛擬記憶體等進程用戶態的資源,也不會切換進程,也就是系統調用過程中一直是同一個進程在運行。系統調用過程也通常稱為特權模式切換

進程上下文切換 和 系統調用的區別?

  1. 進程上下文切換是指,從一個進程切換到另一個進程;系統調用過程一直是同一個進程在運行,屬於進程之內的上下文切換

需要註意的是:進程是由內核來管理和調度的,進程的切換隻能發生在內核態,保存上下文和恢覆上下文的過程並不免費,需要消耗一定資源

  1. 進程的上下文不僅包括了虛擬記憶體、棧、全局變數等用戶空間的資源,還包括了內核堆棧、寄存器等內核空間的狀態。而系統調用這裡沒有涉及到虛擬記憶體等這些進程用戶態的資源

  2. 因此進程的上下文切換就比系統調用時多了一步:在保存當前進程的內核狀態和 CPU 寄存器之前,需要先把該進程的虛擬記憶體、棧等保存下來;而載入了下一進程的內核態後,還需要刷新進程的虛擬記憶體和用戶棧。

進程切換的常見場景

進程切換時需要切換上下文,換句話說,只有在進程調度的時候,才需要切換上下文。Linux 為每個 CPU 都維護了一個就緒隊列,將活躍進程(即正在運行和正在等待 CPU 的進程)按照優先順序和等待 CPU 的時間排序,然後選擇最需要 CPU 的進程,也就是優先順序最高和等待 CPU 時間最長的進程來運行。
進程切換的場景有:

  1. 進程時間片耗盡,為了保證所有進程可以得到公平調度,CPU 時間被劃分為一段段的時間片,這些時間片再被輪流分配給各個進程。當某個進程的時間片耗盡了,就會被系統掛起,切換到其它正在等待 CPU 的進程運行。
  2. 進程在系統資源不足(比如記憶體不足)時,要等到資源滿足後才可以運行,這個時候進程也會被掛起,並由系統調度其他進程運行。
  3. 進程通過睡眠函數 sleep 主動把自己掛起,CPU會重新調度;
  4. 當有CPU發現優先順序更高的進程運行時,為了去運行高優先順序進程,當前進程會被掛起;
  5. 發生硬中斷,CPU 上的進程會被掛起,然後去執行內核中的中斷服務進程。

線程上下文切換

對操作系統來說,進程是資源分配的基本單位,而線程則是任務調度的基本單位。內核中的任務調度實際是在調度線程,進程只是給線程提供虛擬記憶體、全局變數等資源。線程上下文切換時,共用相同的虛擬記憶體和全局變數等資源不需要修改。而線程自己的私有數據,如棧和寄存器等,上下文切換時需要保存。
關於進程和線程的區別:

  • 當進程中只有一個線程時,可以認為進程就等於線程。
  • 當進程擁有多個線程時,這些線程會共用父進程的資源(即共用相同的虛擬記憶體和全局變數等資源)。這些資源在上下文切換時是不需要修改的。
  • 另外,線程也有自己的私有數據,比如棧和寄存器等,這些在上下文切換時也是需要保存的。

因此線程上下文切換有兩種情況:

  • 前後兩個線程屬於不同進程,因為資源不共用,所以切換過程就跟進程上下文切換是一樣的;
  • 前後兩個線程屬於同一個進程,因為虛擬記憶體是共用的,所以在切換時,虛擬記憶體這些資源就保持不動,只需要切換線程的私有數據、寄存器等不共用的數據。

中斷上下文切換

上下文切換有時也因硬體中斷而觸發。硬體中斷是指硬體設備(如鍵盤、滑鼠、調試解調器、系統時鐘)給內核發送的一個信號,該信號表示一個事件(如按鍵、滑鼠移動、從網路連接接收到數據)發生了。
為了快速響應硬體的事件,中斷處理會打斷進程的正常調度和執行,然後調用中斷處理程式,響應設備事件。在打斷其他進程時,需要先將進程當前的狀態保存下來,等中斷結束後,進程仍然可以恢復回來。

跟進程上下文不同,中斷上下文切換不涉及進程的用戶態。所以,即便中斷過程打斷了一個正處在用戶態的進程,也不需要保存和恢復這個進程的虛擬記憶體、全局變數等用戶態資源。中斷上下文,只包括內核態中斷服務程式執行所必需的狀態,也就是 CPU 寄存器、內核堆棧、硬體中斷參數等。

中斷上下文切換並不涉及到進程的用戶態。所以即便中斷過程打斷了一個正處在用戶態的進程,也不需要保存和恢復這個進程的虛擬記憶體、全局變數等用戶態資源。中斷上下文,其實只包括內核態中斷服務程式執行所必須的狀態,包括 CPU 寄存器、內核堆棧、硬體中斷參數等。

對同一個 CPU 來說,中斷處理比進程擁有更高的優先順序,所以中斷上下文切換不會與進程上下文切換同時發生。並且,由於中斷會打斷正常進程的調度和執行,所以大部分中斷處理程式都短小精悍,以便可以儘快完成。

上下文切換的消耗

保存上下文恢覆上下文的過程並不是免費的,需要內核在 CPU 上運行才能完成。據測試,每次上下文切換都需要幾十納秒到數微妙的 CPU 時間。特別是在進程上下文切換次數較多的情況下,很容易導致 CPU 將大量時間消耗在寄存器、內核棧、虛擬記憶體等資源的保存和恢覆上,從而大大縮短了真正運行進程的時間。

Linux相比與其他操作系統(包括其他類 Unix 系統)有很多的優點,其中有一項就是,其上下文切換和模式切換的時間消耗非常少。
Linux 通過 TLB 來管理虛擬記憶體到物理記憶體的映射關係。當虛擬記憶體更新後,TLB 也需要刷新,記憶體的訪問也會隨之變慢。特別是多處理器系統上,緩存是被多個處理器共用的,刷新緩存不僅會影響當前處理器的進程,還會影響共用緩存的其它處理器的進程。所以過多的上下文切換對系統來說意味著會消耗大量的 CPU 時間。

根據Tsuna的測試報告,每次上下文切換都需要幾十納秒到數微妙的CPU時間,這個時間還是相當可觀的。
不管是哪種場景導致的上下文切換,你都應該知道:

  1. CPU上下文切換,是保證Linux系統正常工作的核心功能,一般情況下不需要開發人員特別關註。
  2. 但過多的上下文切換,會把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上下文切換情況

筆者的環境是:Centos7

執行如下的命令: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


本篇文章到這裡就結束啦,很感謝你能看到最後,如果覺得文章對你有幫助,別忘記關註我!更多精彩的文章


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 大家好,先祝大家國慶快樂。不過大家看到這篇文章的時候估計已經過完國慶了 :)。 上一篇我們寫瞭如何通過 SelfContained 模式發佈程式(不安裝運行時運行.NET程式)達到不需要在目標機器上安裝 runtime 就可以運行 .NET 程式的目標。其實除了標準的 self-contained ...
  • .NET下資料庫的負載均衡(有趣實驗)這篇文章發表後,受到了眾多讀者的關註與好評,其中不乏元老級程式員。 讀者來信中詢問最多的一個問題是:它是否能支持“異種資料庫”的負載均衡?? 今天就在此統一回覆:能(暫時只能在.Net6版本下實現。.Net Framwork版本後續會再實現。) 下麵就通過一個例 ...
  • 一:背景 1.講故事 最近遇到一位朋友的程式崩潰,發現崩潰點在富編輯器 msftedit 上,這個不是重點,重點在於發現他已經開啟了 頁堆 ,看樣子是做了最後的掙扎。 0:000> !analyze -v EXCEPTION_RECORD: (.exr -1) ExceptionAddress: 8 ...
  • 一、keepalived是什麼 Keepalived 軟體起初是專為LVS負載均衡軟體設計的,用來管理並監控LVS集群系統中各個服務節點的狀態,後來又加入了可以實現高可用的VRRP功能。因此,Keepalived除了能夠管理LVS軟體外,還可以作為其他服務(例如:Nginx、Haproxy、MySQ ...
  • 大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是一種靈活的i.MXRT下多串列NOR Flash型號選擇的量產方案。 對於以 i.MXRT 這類沒有內部 NVM (Non-Volatile Memory) 的 MCU 為主控的項目來說,為其選配一顆 NVM 作為代碼存儲器是頭等大事, ...
  • 如何設計藝術字?如何進行圖標設計?Art Text 4 Mac版是一款藝術字體製作和圖標設計軟體,它支持多圖層,可以輕鬆創造複雜圖形。可將該程式創建的圖形應用於iWork,Microsoft office、BeLight等應用程式,以及各種其他文本編輯和網頁設計程式。使用Art Text 4 Mac ...
  • @(文章目錄) 前言 上一篇和大家一起分享瞭如何使用LabVIEW OpenCV dnn實現手寫數字識別,今天我們一起來看一下如何使用LabVIEW OpenCV dnn實現圖像分類。 一、什麼是圖像分類? 1、圖像分類的概念 圖像分類,核心是從給定的分類集合中給圖像分配一個標簽的任務。實際上,這意 ...
  • 還在尋找一款好玩的休閑益智游戲嗎?現為大家分享一款經典割繩子游戲Cut the Rope Remastered Mac版,這款游戲的目標很簡單,就是合理的切割繩子,讓小怪物吃到糖果即可過關,但是要想得到高評價,就要想辦法吃到所有的星星。 詳情:經典割繩子游戲Cut the Rope Remaster ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...