眾所周知,Linux是一個支援多任務的作業系統,它能同時運行的任務數量遠遠超過CPU的數量。當然,這些任務其實並不是真正同時運作的(對於單一CPU),而是因為系統會在短時間內將CPU輪流分配給這些任務,從而創造出多個任務同時運作的假象。
在每個任務運行之前,CPU需要知道從哪裡載入和啟動該任務。這意味著系統需要提前設定好CPU的暫存器和程式計數器。
CPU暫存器是內建於CPU中的小型但非常快速的記憶體。而程式計數器用於儲存CPU目前正在執行的指令位置或下一條要執行的指令位置。
這兩者都是CPU在執行任何任務之前所必需的環境,因此被稱為」CPU上下文」。請參考下圖:
知道了 CPU 上下文是什麼,我想你理解 CPU 上下文切換就很容易了。 「CPU上下文切換」指的是先儲存上一個任務的 CPU 上下文(CPU暫存器和程式計數器),然後將新任務的上下文載入到這些暫存器和程式計數器中,最後跳到程式計數器。
這些已儲存的上下文儲存在系統核心中,並在重新安排任務執行時再次載入。這確保了任務的原始狀態不受影響,並且任務似乎持續運行。
你可能會說 CPU 上下文切換無非就是更新 CPU 暫存器和程式計數器值,而這些暫存器是為了快速運行任務而設計的,那為什麼會影響 CPU 效能呢?
在回答這個問題之前,請問,你有沒有想過這些「任務」是什麼?你可能會說一個任務就是一個進程或一個線程。是的,進程和執行緒正是最常見的任務,但除此之外,還有其他類型的任務。
別忘了硬體中斷也是常見的任務,硬體觸發訊號,會引起中斷處理程序的呼叫。
因此,CPU 上下文切換至少有三種不同的類型:
讓我們一一來看看。
#Linux 依照特權等級將行程的運作空間分割為核心空間和使用者空間,分別對應下圖中 Ring 0
和 Ring 3
的 CPU 特權等級的 。
Ring 0
)擁有最高權限,可以直接存取所有資源Ring 3
)只能存取受限資源,不能直接存取記憶體等硬體設備。它必須透過系統呼叫被陷入(trapped)核心中才能存取這些特權資源。 從另一個角度來看,一個行程既可以在使用者空間也可以在核心空間運作。當一個行程在使用者空間運行時,稱為該行程的使用者態,當它落入核心空間時,稱為該行程的內核狀態。
從用戶態到核心態的轉換需要透過系統呼叫來完成。例如,當我們查看一個檔案的內容時,我們需要以下系統呼叫:
open()
:開啟檔案read()
:讀取檔案的內容write()
:將檔案的內容寫入到輸出檔案(包含標準輸出)#close()
:關閉檔案那麼在上述系統呼叫過程中是否會發生 CPU 上下文切換呢?當然是的。
這需要先保存 CPU 暫存器中原來的使用者狀態指令的位置。接下來,為了執行核心態的程式碼,需要將 CPU 暫存器更新到核心態指令的新位置。最後是跳到內核態運行內核任務。
那麼系統呼叫結束後,CPU 暫存器需要恢復原來儲存的使用者狀態,然後切換到使用者空間繼續運作進程。
因此,在一次系統呼叫的過程中,實際上有兩次 CPU 上下文切換。
但要指出的是,系統呼叫進程不會涉及進程切換,也不會涉及虛擬記憶體等系統資源切換。這與我們通常所說的「進程上下文切換」不同。進程上下文切換是指從一個進程切換到另一個進程,而係統呼叫期間始終運行同一個進程
系統呼叫過程通常被稱為特權模式切換,而不是上下文切換。但實際上,在系統呼叫過程中,CPU 的上下文切換也是不可避免的。
那麼進程上下文切換和系統呼叫有什麼差別呢?首先,進程是由核心管理的,進程切換只能發生在核心態。因此,進程上下文不僅包含虛擬記憶體、堆疊和全域變數等使用者空間資源,還包括核心堆疊和暫存器等內核空間的狀態。
所以進程上下文切換比系統呼叫要多出一步:
在儲存目前行程的核心狀態和 CPU 暫存器之前,需要先儲存行程的虛擬記憶體、堆疊等;並載入下一個行程的核心狀態。
根據 Tsuna 的測試報告,每次上下文切換需要數十奈秒至微秒的 CPU 時間。這個時間是相當可觀的,尤其是在大量進程上下文切換的情況下,很容易導致 CPU 花費大量時間來保存和恢復暫存器、核心堆疊、虛擬記憶體等資源。這正是我們在上一篇文章中談到的,一個導致平均負荷上升的重要因素。
那麼,該行程何時會被調度/切換到在 CPU 上運作?其實有很多場景,下面我要為大家總結一下:
suspends itself
through the sleep function, it will naturally be rescheduled. It is very necessary to understand these scenarios, because once there is a performance problem with context switching, they are the killer behind the scenes.
The biggest difference between threads and processes is that threads are the basic unit of task scheduling, while processes are the basic unit of resource acquisition.
To put it bluntly, the so-called task scheduling in the kernel actually schedules threads; and the process only provides resources such as virtual memory and global variables for threads. Therefore, for threads and processes, we can understand it this way:
In this way, thread context switching can actually be divided into two situations:
Obviously, thread switching within the same process consumes less resources than switching multiple processes. This is also the advantage of multi-threading instead of multi-process.
In addition to the previous two context switches, there is another scenario that also outputs CPU context switching, which is interrupt.
In order to quickly respond to events, hardware interrupts will interrupt the normal scheduling and execution process, and then call the interrupt handler.
When interrupting other processes, the current state of the process needs to be saved so that the process can still recover from the original state after the interruption.
Unlike process context, interrupt context switching does not involve the user state of the process. Therefore, even if the interrupt process interrupts the process in user mode, there is no need to save and restore user mode resources such as virtual memory and global variables of the process.
In addition, like process context switching, interrupt context switching will also consume CPU. Excessive switching times will consume a lot of CPU resources and even seriously reduce the overall performance of the system. Therefore, when you notice too many interrupts, you need to pay attention to check whether it will cause serious performance problems for your system.
In summary, no matter which scenario leads to context switching, you should know:
CPU context switching is one of the core functions to ensure the normal operation of the Linux system, and generally does not require our special attention.
However, excessive context switching will consume CPU time to save and restore data such as registers, kernel stacks, virtual memory, etc., thus shortening the actual running time of the process and causing a significant decrease in overall system performance.
以上是探討 Linux CPU 的上下文切換的詳細內容。更多資訊請關注PHP中文網其他相關文章!