1. 程式人生 > 其它 >通過實戰理解CPU上下文切換

通過實戰理解CPU上下文切換

Linux是一個多工的作業系統,可以支援遠大於CPU數量的任務同時執行,但是我們都知道這其實是一個錯覺,真正是系統在很短的時間內將CPU輪流分配給各個程序,給使用者造成多工同時執行的錯覺。所以這就是有一個問題,在每次執行程序之前CPU都需要知道程序從哪裡載入、從哪裡執行,也就是說需要系統提前幫它設定好CPU暫存器和程式計數器

CPU上下文

CPU上下文其實是一些環境正是有這些環境的支撐,任務得以執行,而這些環境的硬體條件便是CPU暫存器和程式計數器。CPU暫存器是CPU內建的容量非常小但是速度極快的儲存裝置,程式計數器則是CPU在執行任何任務時必要的,裡面記錄了當前執行任務的行數等資訊,這就是CPU上下文

CPU上下文切換

根據任務的不同,CPU的上下文切換就可以分為程序上下文切換、執行緒上下文切換、中斷上下文切換

程序上下文切換

在Linux中,Linux按照特權等級,將程序的執行空間分為核心空間和使用者空間:

  • 核心空間具有最高許可權,可以直接訪問所有資源
  • 使用者空間只能訪問受限資源,不能直接訪問記憶體等硬體裝置,要想訪問這些特權資源,必須通過系統呼叫

對於一個程序來說,一般是執行在使用者態的,但是當需要訪問記憶體、磁碟等硬體裝置的時候需要陷入到核心態中,也就是要從使用者態到核心態的轉變,而這種轉變需要通過系統呼叫來實現,例如一個開啟檔案的操作,需要呼叫open()開啟檔案,read()讀取檔案內容,write()將檔案內容輸出到控制檯,最後close()關閉檔案,這就是系統呼叫

在系統呼叫的過程中同樣發發生了CPU上下文切換:

  • CPU暫存器裡面原來使用者態的指令位置,需要先儲存起來,接著執行核心態程式碼
  • CPU暫存器需要更新為核心態指令的位置,執行核心態程式碼

系統呼叫結束後,CPU暫存器需要恢復原來儲存的使用者態,然後切換為使用者空間,所以一次系統呼叫的過程,會發生兩次的CPU上下文切換

但是我們一般說系統呼叫是特權模式切換而不是上下文切換,因為這裡沒有涉及到虛擬記憶體等這些程序使用者態的資源,也不會切換程序是屬於程序之內的上下文切換

程序是由核心來管理和排程的,程序的切換隻能發生在核心態,所以程序的上下文包含了虛擬記憶體、棧、全域性變數等使用者空間的資源,還包含了核心堆疊、暫存器等核心空間的狀態,所以程序的上下文切換要比系統呼叫更多一步,儲存該程序的虛擬記憶體、棧等使用者空間的資源

程序上下文切換一般需要幾十納秒到數微秒的CPU時間,當程序上下文切換次數比較多的情況下愛,將導致CPU將大量的時間耗費在暫存器、核心棧即虛擬記憶體等資源的儲存和恢復上,另外,Linux通過TLB快表來管理虛擬記憶體到實體記憶體的對映關係,當虛擬記憶體更新之後,需要重新整理快取,在這多處理系統上是很複雜的,因為多個處理器共享一個快取

下面再來說說什麼時候會進行程序的上下文切換,其實就是程序在被排程的時候需要切換上下文,可能是主動地,也有可能是被動的

  • 系統程序正常排程演算法導致程序上下文切換,例如目前使用的時間片輪轉演算法,當一個程序的時間片耗盡之後,CPU會進項程序的排程切換到其他程序
  • 程序在資源不足的時候,會被掛起例如在等待IO或者記憶體不足的時候,會主動掛起,並且等待系統排程其他程序
  • 當程序通過一些睡眠函式sleep()主動掛起的時候,也會重新排程
  • 當有高優先順序的程序執行時,當前程序也會被掛起
  • 當發生硬體中斷時,CPU上的程序會被中斷掛起

執行緒上下文切換

執行緒是排程的基本單位,而程序則是資源擁有的基本單位,也就是說對於核心中的任務排程是以執行緒為單位,但是程序只是給執行緒提供了虛擬記憶體、全域性變數等資源,程序與執行緒之間的區別這裡不再介紹

那麼執行緒上下文的切換,其實分為兩種情況:

  • 前後兩個執行緒屬於不同程序,因為資源不共享,所以這時候的執行緒上下文切換和程序上下文切換是一致的
  • 前後兩個執行緒屬於同一個程序,因為虛擬記憶體是共享的,所以在切換的時候,虛擬記憶體這些資源保持不動,只有切換執行緒的私有資料、暫存器等不共享的資源

所以同進程內的執行緒切換要比多程序內的執行緒切換消耗更少的資源

中斷上下文切換

中斷是為了快速響應硬體的事件,簡單來shu就是計算機停下當前的事情,去處理其他的事情,然後在回來繼續執行之前的任務,例如我們在呼叫print函式的時候,其實彙編的底層會幫我們呼叫一條 int 0x80的指令,便是呼叫0x80號中斷

當然,中斷要先將當前程序的狀態儲存下來,這樣中斷結束後進程仍然可以從原來的狀態恢復執行,中斷上下文的切換並不涉及程序的使用者態,所以當中斷程式打斷了正在處於使用者態的程序,不需要儲存和恢復這個程序的虛擬記憶體、全域性變數等使用者態資源,只需要儲存和恢復這個程序的核心態中的資源包括CPU暫存器、核心堆疊等

對於同一個CPU來說,中斷處理比程序擁有更高的優先順序,所以中斷上下文切換並不會與程序上下文切換同時發生,一般來說中斷程式都執行比較快短小精悍,以便快速結束執行之前的任務。當中斷上下文切換次數比較多的時候,會耗費大量的CPU

怎麼檢視系統上下文

上面已經介紹到CPU上下文切換分為程序上下文切換、執行緒上下文切換、中斷上下文切換,那麼過多的上下文切換會把CPU的時間消耗在暫存器、核心棧以及虛擬記憶體等資料的儲存和恢復上,縮短程序真正執行的時間,成為系統性能大幅下降的一個因素

所以我們可以使用vmstat這個工具來查詢系統的上下文切換情況,vmstat是一個常用的系統性能分析工具,可以用來分析CPU上下文切換和中斷的次數

需要特別關注的是:

  • cs(context switch):每秒上下文切換的次數
  • in(interrupt):每秒中斷的次數
  • r(Running or Runnable):就緒佇列的長度,也就是正在執行和等待CPU的程序
  • b(Blocked):處於不可中斷睡眠狀態的程序數

vmstat是給出整個系統總體的上下文切換情況,要想檢視每個程序的詳細情況就需要使用pidstat,加上-w選項就可以檢視程序上下文切換的情況

需要特別關注的是:

  • cswch(voluntary context switches):表示每秒自願上下文切換的次數
  • nvcswch(non voluntary context switches):表示每秒非自願上下文切換的次數

這兩個概念的分別含義:

  • 自願上下文切換:程序無法獲取所需的資源,導致的上下文切換,例如IO、記憶體等資源不足時,就會發生自願上下文切換
  • 非自願上下文切換:程序由於時間片已到等時間,被系統強制排程,進而發生的上下文切換,例如大量的程序都在爭搶CPU時,就容易發生非自願上下文切換

實戰分析

通過上面的工具已經可以初步檢視到系統上下文切換的次數,但是當系統上下文切換的次數為多少時是不正常的呢?

案例使用sysbench工具來模擬多執行緒排程切換的情況,sysbench是一個多執行緒的基準測試工具,可以模擬上下文切換過多的問題

首先在第一個終端執行stsbench,模擬多執行緒切換問題

# 以 10 個執行緒執行 5 分鐘的基準測試,模擬多執行緒切換的問題
sysbench --threads=10 --max-time=300 threads run

然後在第二個終端執行vmstat,每1秒檢視上下文切換的情況

可以觀察到如下指標:

  • r列:就緒佇列的長度已經到了8左右,已經超過了2個cpu,所以會有大量的CPU競爭
  • us(user)列和sy(system)列,這兩列的CPU使用率已經到達100%,並且大量是由sy造成的,說明CPU主要是被核心佔用了
  • in(interrupt):in列的數值也到了解決1萬,所以中斷處理也是一個問題

那我們接著使用pidstat來檢視是那一個程序出現了問題,由於pidstat預設是顯示程序的指標資料,但是我們使用sysbench模擬的執行緒的資料,所以需要加上-t選項

gpw@gopuwe:~$ pidstat -wt

所以到這裡可以分析出是sysbench的子執行緒的上下文切換次數有很多

還有一個問題,在使用vmstat的時候,發現in指標的資料也比較多,那麼我們需要找出是什麼型別的中斷導致了中斷上升,中斷肯定是發生在核心態,但是pidstat只是一個程序的效能分析工具,並不提供任何關於中斷的詳細資訊

我們可以從/proc/interrupts這個只讀檔案中讀取,/proc是一個虛擬檔案系統,用於核心空間和使用者空間之間的通訊,/proc/interrupts則提供了一個只讀的中斷使用情況,可以使用cat命令檢視/proc/interrupts可以發現變化速度最快的是重排程中斷RES,這個中斷型別表示喚醒空閒狀態的CPU來排程新的任務執行,也被成為處理器中斷

那麼到底上下文切換的次數為多少合適呢?

這個數值其實取決於系統本身的 CPU 效能,在我看來,如果系統的上下文切換次數比較穩

定,那麼從數百到一萬以內,都應該算是正常的。但當上下文切換次數超過一萬次,或者切

換次數出現數量級的增長時,就很可能已經出現了效能問題,這個時候還要根據上下文切換的型別,做具體的分析,例如:

  • 自願上下文切換變多了,說明程序都在等待資源,有可能發生了 I/O 等其他問題;
  • 非自願上下文切換變多了,說明程序都在被強制排程,也就是都在爭搶 CPU,說明 CPU的確成了瓶頸;
  • 中斷次數變多了,說明 CPU 被中斷處理程式佔用,還需要通過檢視 /proc/interrupts 檔案

轉自https://www.toutiao.com/i7041467993586434563/?tt_from=weixin&utm_campaign=client_share&wxshare_count=1&timestamp=1639703121&app=news_article&utm_source=weixin&utm_medium=toutiao_android&use_new_style=1&req_id=202112170905200102111792080066EEB1&share_token=54c49e27-fab0-454c-a9f7-d5b879b668c0&group_id=7041467993586434563