上下文切換性能篇

来源:https://www.cnblogs.com/freeweb/archive/2023/07/31/17594628.html
-Advertisement-
Play Games

現代操作系統都是多任務的分時操作系統,也就是說同時響應多個用戶交互或同時支持多個任務處理,因為 CPU 的速度很快而用戶交互的頻率相比會低得多。所以例如在 Linux 中,可以支持遠大於 CPU 數量的任務同時執行,對於單個 CPU 來說,其實任務並不是在同時執行,而是操作系統在很短的時間內,使得多 ...


現代操作系統都是多任務的分時操作系統,也就是說同時響應多個用戶交互或同時支持多個任務處理,因為 CPU 的速度很快而用戶交互的頻率相比會低得多。所以例如在 Linux 中,可以支持遠大於 CPU 數量的任務同時執行,對於單個 CPU 來說,其實任務並不是在同時執行,而是操作系統在很短的時間內,使得多個進程交替獲得 CPU 來執行,由於切換速度比較快,因此這會給我們一種程式同時運行的錯覺,這就是操作系統中的多道程式設計技術,對 CPU 實現了虛擬化,使得我們看起來好像有多個 CPU 來運行任務一樣,我們也稱這多個任務是併發運行的。

每個任務運行時,都有自己的寄存器狀態,主要包括:數據寄存器、地址寄存器、控制和狀態寄存器等。其中地址寄存器又包括:變址寄存器、段指針寄存器、棧指針寄存器等;控制和狀態寄存器主要包括:程式計數器、指令寄存器等,這類寄存器對用戶來說是不可見的。

除了寄存器,進程在記憶體中也會有自己的狀態,操作系統基於這種狀態來管理和控制進程,而這種狀態不允許被用戶直接訪問或修改,例如頁表、進程優先順序、進程 I/O 狀態等。

上面的寄存器狀態和記憶體狀態我們統稱為進程的上下文(context switch),而整個進程主要有 3 部分組成:

  1. 可執行的程式(二進位序列)
  2. 程式運行所需的數據(例如:變數、緩衝區等)
  3. 程式的執行上下文

在多道程式設計中,假如有 A 和 B 兩個進程,當前 B 進程正在執行,這時操作系統需要調度 A 執行,那麼首先需要保存 B 的上下文,然後恢復 A 原來的上下文,恢復的過程會重新設置相關寄存器的值,例如將程式計數器設置為 A 上次執行到的指令地址,這樣 A 就繼續運行。這個由進程 B 切換為進程 A 的過程就叫做進程的上下文切換。

其實上下文切換不僅是進程上下文切換,還包括:線程上下文切換和中斷上下文切換。

系統調用和上下文切換的關係

進程的狀態分為用戶態和內核態,進程在用戶態通過系統調用陷入內核態,那麼在系統調用和上下文切換時什麼關係呢?我們來看一下,首先是要保存進程在用戶態下的寄存器,但並不會保存記憶體中的資源,比如虛擬記憶體、棧、進程式控制制狀態等,然後進入內核態會將 CPU 寄存器的值更新為內核指令的位置,確保正確執行內核中的一段代碼,然後開始執行特權指令,當系統調用執行完成後,CPU 需要恢複原來保存的用戶態寄存器,並切換到用戶空間,繼續運行進程。所以我們看這個過程類似於發生了 1.5 次 CPU 的上下文切換,但是這個切換比較輕量,並不會保存進程的記憶體狀態等資源,也不會對進程進行切換,所以我們一般不說系統調用是上下文切換,但是這個過程中其實是存在和上下文切換類似的過程的,只是開銷比較小一些,所以我們總結系統調用和上下文切換的區別:

  1. 進程上下文切換是多個進程間的切換,而系統調用過程不涉及進程的切換。
  2. 系統調用只進行寄存器的保存和恢復,不涉及進程資源的保存和恢復,因此開銷更小。

進程上下文切換的開銷

通過 lmbench 測試或者第三方的報告可以發現,進程上下文切換的開銷大約在幾十納秒到幾微秒之間,如果切換過於頻繁,那麼很容易導致大量的時間都浪費在寄存器、內核棧以及進程記憶體狀態等資源的保存和恢覆上,從而縮短進程真正運行的時間,由於上下文切換主要由 CPU 完成,因此會直接導致 CPU 負載的升高。

另外,CPU 通過 TLB 來提高虛擬記憶體到物理記憶體的查找性能,通過高速緩存來加速數據的查找,所以進程的上下文切換會導致 TLB 和高速緩存重新刷新,最終導致程式運行性能降低。

雖然上下文切換有一定的性能開銷,但是合理的上下文切換是提升多個進程執行效率的關鍵,通常上下文切換會發生在下麵這些情況中:

  1. 為了保證所有進程都有機會被公平調度,CPU 時間會劃分為一個個的時間片,這些時間片會儘量均勻的分配給各個進程,如果某個進程的時間片耗盡,會通過進程的上下文切換選擇合適的進程獲得進程的時間片繼續運行,合理的時間片可以將上下文切換的開銷給攤銷掉。
  2. 進程在等待資源時,比如發起系統調用需要等待 IO 或者網路完成才可以運行,這個時候操作系統也會調度其他的進程運行。
  3. 進程主動進入睡眠狀態時,也會讓出 CPU 給其他進程。
  4. 如果有優先順序更高的進程加入,優先順序低的進程也可能被掛起,轉而運行優先順序更高的進程。
  5. 發生硬體中斷時,進程會被中斷掛起,從而執行內核的中斷服務程式。

如果系統的上下文切換出現了問題,那麼一定是遇到了上面情況中的某一個或多個。

上下文切換除了上面所說的進程上下文切換,還包括線程上下文切換、中斷上下文切換,下麵簡單來敘述下。

進程是資源管理的基本單位,而線程是調度的基本單位,也就是說內核中任務調度的對象實際上是線程,進程給線程提供了地址空間、全局變數等資源,每個線程擁有自己的棧和寄存器,這樣在同一個進程的多個線程之間做上下文切換時,進程的資源是不需要變化的,只需要對每個線程獨立的棧和寄存器進行切換即可,因此線程上下文切換要比進程上下文切換的開銷更小,這也是多線程相比多進程的優勢所在。但是如果兩個線程屬於不同的進程,這時候線程上下文切換和進程上下文切換的開銷是一樣的。

中斷上下文切換是為了快速響應其他硬體,中斷處理會打斷其他進程的正常調度和執行,然後開始運行中斷處理程式,從而響應設備事件,因此在打斷正常運行的進程時,就需要將進程的寄存器保存下來,這樣在中斷結束後,進程仍然可以從原來的狀態恢復運行。同樣值得註意的是,中斷上下文不涉及進程的用戶態,也就是不需要保存被打斷進程的地址空間、全局變數、用戶棧等資源,因為中斷本身是在內核態執行,僅僅需要用到 CPU 寄存器、內核空間堆棧與硬體中斷參數等,因此只需要保存進程的寄存器、內核堆棧即可,恢復時也只需要恢復進程的內核態資源,所以中斷上下文的處理也比進程上下文切換的開銷更小。

對於同一個 CPU 來說,中斷處理比進程運行本身擁有更高的優先順序,所以中斷上下文切換不會與進程上下文切換同時發生,由於中斷會打斷正常運行的進程,因此必須保證硬體中斷處理程式必須短小精悍,快速執行才可以。

總體來說,正常的上下文切換是保證系統正常運轉的必要條件,但是過多的上下文切換會導致系統性能嚴重降低,需要我們特別註意。

上下文切換常見的排查工具有:vmstatpidstat 等,具體用法這裡不再敘述,只是簡單的給出原理和分析思路。

vmstat 工具可以查看整個系統全局的上下文切換情況,重點關註 r、b、cs、in 這些列。

  1. r 是就緒隊列的長度,包括正在運行和等待 CPU 的進程數。
  2. b 表示處於不可中斷睡眠狀態的進程數,通常是等待 I/O 資源的進程。
  3. cs 表示系統每秒上下文切換的次數。
  4. in 表示每秒中斷的次數。

如果想看每個進程的上下文切換情況,就需要使用 pidstat 這個工具了,使用 -w 參數可以獲得進程上下文切換的情況,主要輸出有:cswch/s 和 nvcswch/s 這兩個參數,分別表示每秒自願上下文切換次數、每秒非自願上下文切換次數,這兩個次數的含義主要如下:

  1. 自願上下文切換表示進程由於等待資源而發生的上下文切換,比如發起 I/O、記憶體、網路等請求時,就會發生自願上下文切換。
  2. 非自願上下文切換則表示進程並沒有等待資源,而是由於時間片已到而被系統強制調度,進而發生的上下文切換。如果大量的進程都在爭搶 CPU 時,就會發生很多非自願上下文切換。

這兩類切換對系統性能有著截然不同的影響,也是需要我們註意的。

性能分析思路

如果系統處在正常運行狀態下,那麼使用 vmstat 看到的上下文切換次數應該不會太大,可能幾十上百最多幾千這樣子,如果飆到了幾十萬,那麼說明系統的上下文切換肯定是不正常的,除了上下文切換次數,我們還可以通過 vmstat 重點關註下麵的現象:

  1. 正常 r 不應該超過 CPU 個數,如果 r 太高則說明有非常多的進程在爭搶 CPU,也就是說 CPU 有可能不夠用了。這個時候可以觀察 us 和 sy 確定 CPU 的占用,或者使用 top 進一步分析。
  2. 通常 b 也不會特別高,如果 b 很高則說明有非常多的進程處在 I/O 等待狀態,這個時候 wa 通常會很高,說明系統存在 I/O 瓶頸。
  3. in 如果比較高,達到幾萬或幾十萬,說明中斷過於頻繁,需要註意排查。

然後我們想進一步找到引起問題的進程,那麼可以繼續使用 pidstat 來分析,不過 pidstat 預設只能看到進程級別的,如果是多線程應用註意添加 -t 參數來顯示線程的信息,也就是 pidstat -wt <time>,然後就可以通過分析自願上下文切換和非自願上下文切換來確定進程本身存在的問題。

如果上面發現 wa 特別高,懷疑是硬碟的問題,則可以使用 iotopdstat 進一步分析硬碟相關的瓶頸。

如果上面發現中斷特別高,則可以通過內核文件 /proc/interrupts 查看,例如下麵的命令:

watch -d cat /proc/interrupts

這樣可以查看不同類型的中斷變化情況,具體內容和含義可以查看 Linux kernel 的文檔:https://docs.kernel.org/filesystems/proc.html#kernel-data 其中 interrupts 部分的內容,我們通過統計可以發現具體中斷的原因。

我們對上下文切換和含義與性能做了比較詳細的分析,下麵我們來整理一下相關的知識點:

context-switch


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

-Advertisement-
Play Games
更多相關文章
  • ## 教程簡介 Django是一個開放源代碼的Web應用框架,由Python寫成。採用了MTV的框架模式,即模型M,視圖V和模版T。它最初是被開發來用於管理勞倫斯出版集團旗下的一些以新聞內容為主的網站的,即是CMS(內容管理系統)軟體。Django是高水準的Python編程語言驅動的一個開源模型.視 ...
  • 《quarkus依賴註入》系列聚焦quarkus框架下bean的創建、使用、配置等場景的知識點,本文是系列的開篇,介紹CDI,實戰創建bean ...
  • 本篇介紹的是`pandas`選擇列數據的一個小技巧。之前已經介紹了很多選擇列數據的方式,比如`loc`,`iloc`函數,按列名稱選擇,按條件選擇等等。 這次介紹的是按照列的**數據類型**來選擇列,按類型選擇列可以幫助你快速選擇正確的數據類型,提高數據分析的效率。 # 1. 類型種類 `panda ...
  • 1.前言 眾所周知,Java是一門跨平臺語言,針對不同的操作系統有不同的實現。本文從一個非常簡單的api調用來看看Java具體是怎麼做的. 2.源碼分析 從FileInputStream.java中看到readBytes最後是native調用 /** * Reads a subarray as a ...
  • # RabbitMQ延時隊列和死信隊列 # 延時隊列和死信隊列 > 延時隊列是RabbitMQ中的一種特殊隊列,它可以在消息到達隊列後延遲一段時間再被消費。 > > 延時隊列的實現原理是通過使用消息的過期時間和死信隊列來實現。當消息被髮送到延時隊列時,可以為消息設置一個過期時間,這個過期時間決定了消 ...
  • ASP.NET 團隊和社區在 .NET 8 繼續全力投入 Blazor,為它帶來了非常多的新特性,特別是在服務端渲染(SSR)方面,一定程度解決之前 WASM 載入慢,Server 性能不理想等局限性,也跟原來的 MVC,Razor Pages 框架在底層完成了統一。 AntDesign Blazo ...
  • 在C#中,數據類型分為值類型和引用類型兩種。 引用類型變數存儲的是數據的引用,數據存儲在數據堆中,而值類型變數直接存儲數據。對於引用類型,兩個變數可以引用同一個對象。因此,對一個變數的操作可能會影響另一個變數引用的對象。對於值類型,每個變數都有自己的數據副本,並且對一個變數的操作不可能影響另一個變數 ...
  • 博客推行版本更新,成果積累制度,已經寫過的博客還會再次更新,不斷地琢磨,高質量高數量都是要追求的,工匠精神是學習必不可少的精神。因此,大家有何建議歡迎在評論區踴躍發言,你們的支持是我最大的動力,你們敢投,我就敢肝 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...