Linux性能優化之CPU上下文切換上

来源:https://www.cnblogs.com/LHXW/archive/2018/11/28/10033882.html
-Advertisement-
Play Games

進程在競爭 CPU 的時候並沒有真正運行,為什麼還會導致系統的負載升高 呢?看到今天的主題,你應該已經猜到了,CPU 上下文切換就是罪魁禍首。 我們都知道,Linux 是一個多任務操作系統,它支持遠大於 CPU 數量的任務同時運行。當然, 這些任務實際上並不是真的在同時運行,而是因為系統在很短的時間 ...


 

進程在競爭 CPU 的時候並沒有真正運行,為什麼還會導致系統的負載升高 呢?看到今天的主題,你應該已經猜到了,CPU 上下文切換就是罪魁禍首。 我們都知道,Linux 是一個多任務操作系統,它支持遠大於 CPU 數量的任務同時運行。當然, 這些任務實際上並不是真的在同時運行,而是因為系統在很短的時間內,將 CPU 輪流分配給它 們,造成多任務同時運行的錯覺。 而在每個任務運行前,CPU 都需要知道任務從哪裡載入、又從哪裡開始運行,也就是說,需要 系統事先幫它設置好 CPU 寄存器和程式計數器(Program Counter,PC)。

CPU 寄存器,是 CPU 內置的容量小、但速度極快的記憶體。而程式計數器,則是用來存儲 CPU 正在執行的指令位置、或者即將執行的下一條指令位置。它們都是 CPU 在運行任何任務前,必 須的依賴環境,因此也被叫做 CPU 上下文

 

知道了什麼是 CPU 上下文,我想你也很容易理解 CPU 上下文切換。CPU 上下文切換,就是先 把前一個任務的 CPU 上下文(也就是 CPU 寄存器和程式計數器)保存起來,然後載入新任務 的上下文到這些寄存器和程式計數器,最後再跳轉到程式計數器所指的新位置,運行新任務。

而這些保存下來的上下文,會存儲在系統內核中,併在任務重新調度執行時再次載入進來。這樣 就能保證任務原來的狀態不受影響,讓任務看起來還是連續運行。 我猜肯定會有人說,CPU 上下文切換無非就是更新了 CPU 寄存器的值嘛,但這些寄存器,本身 就是為了快速運行任務而設計的,為什麼會影響系統的 CPU 性能呢?

關於這個問題前,不知道你有沒有想過,操作系統管理的這些“任務”到底是什麼呢? 也許你會說,任務就是進程,或者說任務就是線程。是的,進程和線程正是最常見的任務。但是 除此之外,還有沒有其他的任務呢? 不要忘了,硬體通過觸發信號,會導致中斷處理程式的調用,也是一種常見的任務。 所以,根據任務的不同,CPU 的上下文切換就可以分為幾個不同的場景,也就是進程上下文切 換、線程上下文切換以及中斷上下文切換。怎麼理解這幾個不同的上下文切換,以及它們為什麼會引發 CPU 性能 相關問題

進程上下文切換

Linux 按照特權等級,把進程的運行空間分為內核空間和用戶空間,分別對應著下圖中, CPU 特權等級的 Ring 0 和 Ring 3。

  •    內核空間(Ring 0)具有最高許可權,可以直接訪問所有資源;
  •    用戶空間(Ring 3)只能訪問受限資源,不能直接訪問記憶體等硬體設備,必須通過系統調用 陷入到內核中,才能訪問這些特權資源。

 

換個角度看,也就是說,進程既可以在用戶空間運行,又可以在內核空間中運行。進程在用戶空 間運行時,被稱為進程的用戶態,而陷入內核空間的時候,被稱為進程的內核態。

從用戶態到內核態的轉變,需要通過系統調用來完成。比如,當我們查看文件內容時,就需要多 次系統調用來完成:首先調用 open() 打開文件,然後調用 read() 讀取文件內容,並調用 write() 將內容寫到標準輸出,最後再調用 close() 關閉文件。

那麼,系統調用的過程有沒有發生 CPU 上下文的切換呢?答案自然是肯定的。

CPU 寄存器里原來用戶態的指令位置,需要先保存起來。接著,為了執行內核態代碼,CPU 寄存器需要更新為內核態指令的新位置。最後才才是跳轉到內核態運行內核任務

而系統調用結束後,CPU 寄存器需要恢複原來保存的用戶態,然後再切換到用戶空間,繼續運行進程。所以,一次系統調用的過程,其實是發生了兩次 CPU 上下文切換。 不過,需要註意的是,系統調用過程中,並不會涉及到虛擬記憶體等進程用戶態的資源,也不會切 換進程。這跟我們通常所說的進程上下文切換是不一樣的:

  • 進程上下文切換,是指從一個進程切換到另一個進程運行。
  • 而系統調用過程中一直是同一個進程在運行。

所以,系統調用過程通常稱為特權模式切換,而不是上下文切換。但實際上,系統調用過程中, CPU 的上下文切換還是無法避免的。

那麼,進程上下文切換跟系統調用又有什麼區別呢?

首先,你需要知道,進程是由內核來管理和調度的,進程的切換隻能發生在內核態

所以,進程 的上下文不僅包括了虛擬記憶體、棧、全局變數等用戶空間的資源,還包括了內核堆棧、寄存器等 內核空間的狀態。

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

如下圖所示,保存上下文和恢覆上下文的過程並不是“免費”的,需要內核在 CPU 上運行才能 完成。

 

根據 Tsuna 的測試報告,每次上下文切換都需要幾十納秒到數微妙的 CPU 時間。這個時間還是 相當可觀的,特別是在進程上下文切換次數較多的情況下,很容易導致 CPU 將大量時間耗費在 寄存器、內核棧以及虛擬記憶體等資源的保存和恢覆上,進而大大縮短了真正運行進程的時間。這 也正是上一節中我們所講的,導致平均負載升高的一個重要因素。

另外,我們知道, Linux 通過 TLB(Translation Lookaside Buffer)來管理虛擬記憶體到物理內 存的映射關係。當虛擬記憶體更新後,TLB 也需要刷新,記憶體的訪問也會隨之變慢。特別是在多處 理器系統上,緩存是被多個處理器共用的,刷新緩存不僅會影響當前處理器的進程,還會影響共 享緩存的其他處理器的進程。

知道了進程上下文切換潛在的性能問題後,我們再來看,究竟什麼時候會切換進程上下文。

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

那麼,進程在什麼時候才會被調度到 CPU 上運行呢? 

最容易想到的一個時機,就是進程執行完終止了,它之前使用的 CPU 會釋放出來,這個時候再 從就緒隊列里,拿一個新的進程過來運行。其實還有很多其他場景,也會觸發進程調度,在這裡 我給你逐個梳理下。

其一,為了保證所有進程可以得到公平調度,CPU 時間被劃分為一段段的時間片,這些時間片 再被輪流分配給各個進程。這樣,當某個進程的時間片耗盡了,就會被系統掛起,切換到其它正 在等待 CPU 的進程運行。

其二,進程在系統資源不足(比如記憶體不足)時,要等到資源滿足後才可以運行,這個時候進程 也會被掛起,並由系統調度其他進程運行。

其三,當進程通過睡眠函數 sleep 這樣的方法將自己主動掛起時,自然也會重新調度。

其四,當有優先順序更高的進程運行時,為了保證高優先順序進程的運行,當前進程會被掛起,由高 優先順序進程來運行。

最後一個,發生硬體中斷時,CPU 上的進程會被中斷掛起,轉而執行內核中的中斷服務程式。 瞭解這幾個場景是非常有必要的,因為一旦出現上下文切換的性能問題,它們就是幕後凶手。

線程上下文切換

說完了進程的上下文切換,我們再來看看線程相關的問題。

線程與進程最大的區別在於,線程是調度的基本單位,而進程則是資源擁有的基本單位。說白 了,所謂內核中的任務調度,實際上的調度對象是線程;而進程只是給線程提供了虛擬記憶體、全 局變數等資源。所以,對於線程和進程,我們可以這麼理解:

當進程只有一個線程時,可以認為進程就等於線程。

當進程擁有多個線程時,這些線程會共用相同的虛擬記憶體和全局變數等資源。這些資源在上 下文切換時是不需要修改的。

另外,線程也有自己的私有數據,比如棧和寄存器等,這些在上下文切換時也是需要保存 的。 這麼一來,線程的上下文切換其實就可以分為兩種情況:

第一種, 前後兩個線程屬於不同進程。此時,因為資源不共用,所以切換過程就跟進程上下文 切換是一樣。

第二種,前後兩個線程屬於同一個進程。此時,因為虛擬記憶體是共用的,所以在切換時,虛擬內 存這些資源就保持不動,只需要切換線程的私有數據、寄存器等不共用的數據。

到這裡你應該也發現了,雖然同為上下文切換,但同進程內的線程切換,要比多進程間的切換消 耗更少的資源,而這,也正是多線程代替多進程的一個優勢。

中斷上下文切換

除了前面兩種上下文切換,還有一個場景也會切換 CPU 上下文,那就是中斷。

為了快速響應硬體的事件,中斷處理會打斷進程的正常調度和執行,轉而調用中斷處理程式,響 應設備事件。而在打斷其他進程時,就需要將進程當前的狀態保存下來,這樣在中斷結束後,進 程仍然可以從原來的狀態恢復運行。

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

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

另外,跟進程上下文切換一樣,中斷上下文切換也需要消耗 CPU,切換次數過多也會耗費大量 的 CPU,甚至嚴重降低系統的整體性能。所以,當你發現中斷次數過多時,就需要註意去排查 它是否會給你的系統帶來嚴重的性能問題。

小結 總結一下,不管是哪種場景導致的上下文切換,你都應該知道:

1. CPU 上下文切換,是保證 Linux 系統正常工作的核心功能之一,一般情況下不需要我們特別關註

2. 但過多的上下文切換,會把 CPU 時間消耗在寄存器、內核棧以及虛擬記憶體等數據的保存和恢覆上,從而縮短進程真正運行的時間,導致系統的整體性能大幅下降。

 


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

-Advertisement-
Play Games
更多相關文章
  • 什麼是cron? Cron是linux系統中用來定期執行或指定程式任務的一種服務或軟體。與它相關的有兩個工具:crond 和 crontab。crond 就是 cron 在系統內的宿主程式,crontab 是管理 cron 任務的管理工具。一般情況下,我們安裝完centos5/6 linux系操作系 ...
  • chattr: 加鎖文件,無修改,無刪除許可權。 常用參數: +a: 可給文件追加內容,但無法刪除。 +i 加鎖文件(文件不能被刪除、改名、設定鏈接關係,同時不能寫入或追加內容) -i 解鎖文件(與+i相反) 常用參數用法: 加鎖:chattr +i 文件 查看加鎖: lsattr 文件 +i 加鎖文 ...
  • write 作用:給其它的線上用戶發送消息 格式:write [ 用戶名 ] [ tty ] 註意點:使用之前最好使用 who 命令查看當前線上用戶,tty 為埠號 使用舉例: 在游標閃爍的地方輸入內容,使用 CTRL+D 保存結束髮送 wall 作用:以廣播的方式向系統中所有用戶發送消息 格式: ...
  • 自考本科,操作系統是管理電腦硬體與軟體資源的電腦程式,同時也是電腦系統的內核與基石。操作系統需要處理如管理與配置記憶體、決定系統資源供需的優先次序、控制輸入與輸出設備、操作網路與管理文件系統等基本事務。 ...
  • 1.NIS部分 1.1 簡介 NIS(Network Information Service,or Yellow Page or YP) 網路信息服務,由sun公司開發並授權給unix供應商,最初稱為黃頁,簡稱YP,由於 British Telecom PLC公司優先註冊了Yellow Page商標 ...
  • 一、命令分:內部命令、外部命令① 內部命令:是由 Shell解釋器解釋的② 外部命令:除了Shell解釋器以外的命令③ 識別命令類型:type 命令字 二、命令一般組成格式: 命令字 [選項].. [參數1][ 參數2]… 三、查看的命令: ls 查看方式 某個目錄/多個目錄① 命令字: ls 命令 ...
  • 學習linux之前先瞭解一下操作系統: 操作系統的定義: 操作系統(英語:operating system,縮寫作 OS)是管理電腦硬體與軟體資源的電腦程式,同時也是電腦系統的內核與基石。操作系統需要處理如管理與配置記憶體、決定系統資源供需的優先次序、控制輸入與輸出設備、操作網路與管理文件系統等 ...
  • 伺服器,也稱伺服器,是提供計算服務的設備。由於伺服器需要響應服務請求,併進行處理,因此一般來說伺服器應具備承擔服務並且保障服務的能力。 伺服器: 伺服器指的是網路中能對其他機器提供某些服務的電腦系統,相對普通PC,伺服器指的是高性能電腦,穩定性、安全性要求更高伺服器的高性能體現在高速的運轉能力, ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...