Linux 效能優化之 CPU 篇 ----- 上下文切換

bossaiguo發表於2020-06-25

名詞解釋

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

  2. CPU 上下文切換,就是先把前一個任務的 CPU 上下文(也就是 CPU 暫存器和程式計數器)儲存起來,然後載入新任務的上下文到這些暫存器和程式計數器,最後再跳轉到程式計數器所指的新位置,執行新任務。

  3. Linux 按照特權等級,把程式的執行空間分為核心空間和使用者空間,分別對應著下圖中, CPU 特權等級的 Ring 0 和 Ring 3。

    • 核心空間(Ring 0)具有最高許可權,可以直接訪問所有資源;
    • 使用者空間(Ring 3)只能訪問受限資源,不能直接訪問記憶體等硬體裝置,必須通過系統呼叫陷入到核心中,才能訪問這些特權資源
    • 換個角度看,也就是說,程式既可以在使用者空間執行,又可以在核心空間中執行。程式在使用者空間執行時,被稱為程式的使用者態,而陷入核心空間的時候,被稱為程式的核心態。
  4. 中斷上下文切換, 為了快速響應硬體的事件,中斷處理會打斷程式的正常排程和執行,轉而呼叫中斷處理程式,響應裝置事件。而在打斷其他程式時,就需要將程式當前的狀態儲存下來,這樣在中斷結束後,程式仍然可以從原來的狀態恢復執行。

答疑

  1. 系統呼叫的過程有沒有發生 CPU 上下文的切換呢?

    • CPU 暫存器裡原來使用者態的指令位置,需要先儲存起來。接著,為了執行核心態程式碼,CPU 暫存器需要更新為核心態指令的新位置。最後才是跳轉到核心態執行核心任務。
    • 而系統呼叫結束後,CPU 暫存器需要恢復原來儲存的使用者態,然後再切換到使用者空間,繼續執行程式。所以,一次系統呼叫的過程,其實是發生了兩次 CPU 上下文切換
    • 系統呼叫過程通常稱為特權模式切換,而不是上下文切換
  2. 程式上下文切換跟系統呼叫又有什麼區別呢?

    • 程式是由核心來管理和排程的,程式的切換隻能發生在核心態。所以,程式的上下文不僅包括了虛擬記憶體、棧、全域性變數等使用者空間的資源,還包括了核心堆疊、暫存器等核心空間的狀態。
    • 因此,程式的上下文切換就比系統呼叫時多了一步:在儲存當前程式的核心狀態和 CPU 暫存器之前,需要先把該程式的虛擬記憶體、棧等儲存下來;而載入了下一程式的核心態後,還需要重新整理程式的虛擬記憶體和使用者棧。
  3. 什麼時候會切換程式上下文?

    • 只有在程式排程的時候,才需要切換上下文。Linux 為每個 CPU 都維護了一個就緒佇列,將活躍程式(即正在執行和正在等待 CPU 的程式)按照優先順序和等待 CPU 的時間排序,然後選擇最需要 CPU 的程式,也就是優先順序最高和等待 CPU 時間最長的程式來執行。
  4. 程式在什麼時候才會被排程到 CPU 上執行呢?

    • 為了保證所有程式可以得到公平排程,CPU 時間被劃分為一段段的時間片,這些時間片再被輪流分配給各個程式。這樣,當某個程式的時間片耗盡了,就會被系統掛起,切換到其它正在等待 CPU 的程式執行
    • 程式在系統資源不足(比如記憶體不足)時,要等到資源滿足後才可以執行,這個時候程式也會被掛起,並由系統排程其他程式執行。
    • 當程式通過睡眠函式 sleep 這樣的方法將自己主動掛起時,自然也會重新排程。
    • 當有優先順序更高的程式執行時,為了保證高優先順序程式的執行,當前程式會被掛起,由高優先順序程式來執行。
    • 當硬體中斷時,CPU 上的程式會被中斷掛起,轉而執行核心中的中斷服務程式。
  5. 執行緒與程式的區別是什麼?

    • 執行緒是排程的基本單位,而程式則是資源擁有的基本單位。說白了,所謂核心中的任務排程,實際上的排程物件是執行緒;而程式只是給執行緒提供了虛擬記憶體、全域性變數等資源
    • 當程式只有一個執行緒時,可以認為程式就等於執行緒
    • 當程式擁有多個執行緒時,這些執行緒會共享相同的虛擬記憶體和全域性變數等資源。這些資源在上下文切換時是不需要修改的
    • 另外,執行緒也有自己的私有資料,比如棧和暫存器等,這些在上下文切換時也是需要儲存的

小技巧與工具


# 每隔5秒輸出1組資料
$ vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 7005360  91564 818900    0    0     0     0   25   33  0  0 100  0  0

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

# 每隔5秒輸出1組資料
$ pidstat -w 5
Linux 4.15.0 (ubuntu)  09/23/18  _x86_64_  (2 CPU)

08:18:26      UID       PID   cswch/s nvcswch/s  Command
08:18:31        0         1      0.20      0.00  systemd
08:18:31        0         8      5.40      0.00  rcu_sched

cswch ,表示每秒自願上下文切換(voluntary context switches)的次數.
nvcswch ,表示每秒非自願上下文切換(non voluntary context switches)的次數。
...
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章