名詞解釋
CPU 暫存器,是 CPU 內建的容量小、但速度極快的記憶體。而程式計數器,則是用來儲存 CPU 正在執行的指令位置、或者即將執行的下一條指令位置。它們都是 CPU 在執行任何任務前,必須的依賴環境,因此也被叫做 CPU 上下文
CPU 上下文切換,就是先把前一個任務的 CPU 上下文(也就是 CPU 暫存器和程式計數器)儲存起來,然後載入新任務的上下文到這些暫存器和程式計數器,最後再跳轉到程式計數器所指的新位置,執行新任務。
Linux 按照特權等級,把程式的執行空間分為核心空間和使用者空間,分別對應著下圖中, CPU 特權等級的 Ring 0 和 Ring 3。
- 核心空間(Ring 0)具有最高許可權,可以直接訪問所有資源;
- 使用者空間(Ring 3)只能訪問受限資源,不能直接訪問記憶體等硬體裝置,必須通過系統呼叫陷入到核心中,才能訪問這些特權資源
- 換個角度看,也就是說,程式既可以在使用者空間執行,又可以在核心空間中執行。程式在使用者空間執行時,被稱為程式的使用者態,而陷入核心空間的時候,被稱為程式的核心態。
中斷上下文切換, 為了快速響應硬體的事件,中斷處理會打斷程式的正常排程和執行,轉而呼叫中斷處理程式,響應裝置事件。而在打斷其他程式時,就需要將程式當前的狀態儲存下來,這樣在中斷結束後,程式仍然可以從原來的狀態恢復執行。
答疑
系統呼叫的過程有沒有發生 CPU 上下文的切換呢?
- CPU 暫存器裡原來使用者態的指令位置,需要先儲存起來。接著,為了執行核心態程式碼,CPU 暫存器需要更新為核心態指令的新位置。最後才是跳轉到核心態執行核心任務。
- 而系統呼叫結束後,CPU 暫存器需要恢復原來儲存的使用者態,然後再切換到使用者空間,繼續執行程式。所以,一次系統呼叫的過程,其實是發生了兩次 CPU 上下文切換
- 系統呼叫過程通常稱為特權模式切換,而不是上下文切換
程式上下文切換跟系統呼叫又有什麼區別呢?
- 程式是由核心來管理和排程的,程式的切換隻能發生在核心態。所以,程式的上下文不僅包括了虛擬記憶體、棧、全域性變數等使用者空間的資源,還包括了核心堆疊、暫存器等核心空間的狀態。
- 因此,程式的上下文切換就比系統呼叫時多了一步:在儲存當前程式的核心狀態和 CPU 暫存器之前,需要先把該程式的虛擬記憶體、棧等儲存下來;而載入了下一程式的核心態後,還需要重新整理程式的虛擬記憶體和使用者棧。
什麼時候會切換程式上下文?
- 只有在程式排程的時候,才需要切換上下文。Linux 為每個 CPU 都維護了一個就緒佇列,將活躍程式(即正在執行和正在等待 CPU 的程式)按照優先順序和等待 CPU 的時間排序,然後選擇最需要 CPU 的程式,也就是優先順序最高和等待 CPU 時間最長的程式來執行。
程式在什麼時候才會被排程到 CPU 上執行呢?
- 為了保證所有程式可以得到公平排程,CPU 時間被劃分為一段段的時間片,這些時間片再被輪流分配給各個程式。這樣,當某個程式的時間片耗盡了,就會被系統掛起,切換到其它正在等待 CPU 的程式執行
- 程式在系統資源不足(比如記憶體不足)時,要等到資源滿足後才可以執行,這個時候程式也會被掛起,並由系統排程其他程式執行。
- 當程式通過睡眠函式 sleep 這樣的方法將自己主動掛起時,自然也會重新排程。
- 當有優先順序更高的程式執行時,為了保證高優先順序程式的執行,當前程式會被掛起,由高優先順序程式來執行。
- 當硬體中斷時,CPU 上的程式會被中斷掛起,轉而執行核心中的中斷服務程式。
執行緒與程式的區別是什麼?
- 執行緒是排程的基本單位,而程式則是資源擁有的基本單位。說白了,所謂核心中的任務排程,實際上的排程物件是執行緒;而程式只是給執行緒提供了虛擬記憶體、全域性變數等資源
- 當程式只有一個執行緒時,可以認為程式就等於執行緒
- 當程式擁有多個執行緒時,這些執行緒會共享相同的虛擬記憶體和全域性變數等資源。這些資源在上下文切換時是不需要修改的
- 另外,執行緒也有自己的私有資料,比如棧和暫存器等,這些在上下文切換時也是需要儲存的
小技巧與工具
# 每隔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 協議》,轉載必須註明作者和本文連結