1. CPU 使用率
除了空閒時間外的其他時間佔總 CPU 時間的百分比,就是CPU 使用率,即 1- 空閒時間/CPU 總時間。
當計算 CPU 使用率時,我們通常使用 /proc/stat 檔案中的資料。該檔案提供了有關 CPU 的計數器資訊,包括各種狀態下的節拍數。透過 cat /proc/stat 命令就可詳細檢視其資訊,其中各
列的含義如下:
-
user(通常縮寫為 us),代表使用者態 CPU 時間。注意,它不包括下面的 nice 時間,但包括了 guest 時間。
-
nice(通常縮寫為 ni),代表低優先順序使用者態 CPU 時間,也就是程式的 nice 值被調整為 1-19 之間時的 CPU 時間。這裡注意,nice 可取值範圍是 -20 到 19,數值越大,優先順序反而越低。
-
system(通常縮寫為sys),代表核心態 CPU 時間。
-
idle(通常縮寫為id),代表空閒時間。注意,它不包括等待 I/O 的時間(iowait)。
-
iowait(通常縮寫為 wa),代表等待 I/O 的 CPU 時間。
-
irq(通常縮寫為 hi),代表處理硬中斷的 CPU 時間。
-
softirq(通常縮寫為 si),代表處理軟中斷的 CPU 時間。
-
steal(通常縮寫為 st),代表當系統執行在虛擬機器中的時候,被其他虛擬機器佔用的 CPU 時間。
-
guest(通常縮寫為 guest),代表透過虛擬化執行其他作業系統的時間,也就是執行虛擬機器的 CPU 時間。
-
guest_nice(通常縮寫為 gnice),代表以低優先順序執行虛擬機器的時間。
2. 軟中斷
假設你正在使用一款音樂播放器應用程式,同時還在進行其他任務,如瀏覽網頁。在某個時刻,音樂播放器應用程式需要通知你有一條新的訊息到達。然而,應用程式不能直接中斷你正在瀏覽的網頁,因為這會干擾你的體驗。因此,應用程式使用軟中斷來通知你。
當新的訊息到達時,音樂播放器應用程式觸發一個軟中斷,類似於傳送一個"通知"。這個軟中斷會暫停當前正在執行的任務(瀏覽網頁),然後轉移控制權到應用程式內部的中斷處理程式。在中斷處理程式中,應用程式可以執行特定的操作,比如彈出一個訊息通知,顯示新訊息的內容。
一旦中斷處理程式完成執行,控制權將返回給瀏覽器應用程式,你可以繼續瀏覽網頁,並在螢幕上看到新訊息的通知。
這個例子中,軟中斷允許應用程式在後臺處理事件,並以非侵入性的方式通知使用者。它提供了應用程式與作業系統之間的通訊和協調,而無需直接中斷正在執行的任務。軟中斷的關鍵特點是它由軟體觸發和處理,而不是由外部裝置觸發。
在實際的計算機系統中,軟中斷用於各種目的,如處理網路資料包、處理磁碟操作、定時器事件等。它們提供了一種有效的方式來處理非同步事件,同時保持系統的響應性和穩定性。
網路卡接收到資料包後,會透過硬體中斷的方式,通知核心有新的資料到了。這時,核心就應該呼叫中斷處理程式來響應它。網路卡接收到資料包後,會透過硬體中斷的方式,通知核心有新的資料到了。這時,核心就應該呼叫中斷處理程式來響應它。最後再傳送一個軟中斷訊號,需要從記憶體中找到網路資料,再按照網路協議棧,對資料進行逐層解析和處理,直到把它送給應用程式。
3. 記憶體對映
在現代作業系統中,包括Linux,記憶體管理採用了虛擬記憶體的概念。每個程式都有自己的虛擬地址空間,它是一個抽象的地址空間,與實際的實體記憶體地址是分離的。虛擬地址是由程式使用的,而不是直接對映到實體記憶體。
記憶體對映透過將虛擬記憶體地址對映到實體記憶體地址,將虛擬地址空間與實體記憶體關聯起來。這樣,程式可以使用虛擬地址來訪問記憶體,而無需關心實際的實體記憶體位置。
為了實現這種對映,作業系統維護了一個頁表,它是一個資料結構,記錄了虛擬地址與實體地址之間的對映關係。每個程式都有自己的頁表,用於管理它的虛擬地址空間。
當程式訪問一個虛擬地址時,作業系統會使用頁表查詢對應的實體地址。如果存在對映關係,作業系統將虛擬地址轉換為對應的實體地址,以便在實體記憶體中讀寫資料。如果沒有對映關係,則會觸發頁面錯誤(page fault),作業系統會根據情況從磁碟中載入資料到實體記憶體,並更新頁表以建立對映關係。
記憶體對映在檔案操作中也使用了類似的機制。當將檔案內容對映到程式的地址空間時,作業系統會將檔案的內容對映到一段虛擬地址空間,這樣程式就可以透過讀寫這些虛擬地址來實現對檔案的訪問。
因此,記憶體對映不僅用於將虛擬記憶體地址對映到實體記憶體地址,也用於將檔案內容對映到程式的地址空間中。頁表是維護這些對映關係的重要資料結構,它記錄了虛擬地址與實體地址之間的對應關係。
4. 虛擬記憶體空間分佈
-
程式碼段(Text Segment):
程式碼段包含了可執行程式的機器指令。它通常是隻讀的,因為指令不應該被修改。當程式被載入到記憶體中時,程式碼段被對映到程式的虛擬記憶體空間,以供執行。 -
資料段(Data Segment):
資料段包含了全域性變數和靜態變數的記憶體空間。它通常是可讀寫的。資料段在程式執行時初始化,並且在整個程式的生命週期中保持不變。 -
堆(Heap):
堆是動態分配記憶體的區域。在堆中,程式可以透過呼叫諸如malloc()和free()等函式來動態地分配和釋放記憶體。堆的大小可以在執行時進行調整。 -
棧(Stack):
棧用於儲存區域性變數、函式呼叫和函式引數。棧是以後進先出(LIFO)的方式進行管理的。每當呼叫函式時,棧會分配一段記憶體來儲存該函式的區域性變數和其他相關資訊。當函式返回時,這些記憶體將被釋放。 -
共享庫區域(Shared Library Region):
共享庫區域包含了共享庫的程式碼和資料。這些庫可以由多個程式共享,以節省記憶體空間。當多個程式使用相同的共享庫時,它們可以將該庫對映到自己的虛擬記憶體空間中,以便共享使用。 -
核心空間(Kernel Space):
核心空間是由作業系統核心使用的虛擬記憶體區域。它包含了作業系統核心的程式碼、資料和驅動程式等。使用者程式無法直接訪問核心空間,需要透過系統呼叫等特定介面與核心進行互動。
5. 記憶體分配
記憶體分配:
-
靜態分配:靜態分配是指在編譯時或程式啟動時就確定記憶體的分配情況。例如,全域性變數和靜態變數的記憶體分配在程式載入時完成,並在整個程式的生命週期中保持不變。
-
動態分配:動態分配是在程式執行時根據需要動態分配和釋放記憶體。常用的動態記憶體分配函式是malloc()、calloc()和realloc()。這些函式允許你請求一定大小的記憶體塊,並返回一個指向該記憶體塊的指標。動態分配的記憶體塊位於堆(Heap)區域。
記憶體回收:
-
回收快取,比如使用 LRU(Least Recently Used)演演算法,回收最近使用最少的記憶體頁面;
-
回收不常訪問的記憶體,把不常用的記憶體透過交換分割槽直接寫到磁碟中;
-
殺死程式,記憶體緊張時系統還會透過 OOM(Out of Memory),直接殺掉佔用大量記憶體的程式。
其中,第二種方式回收不常訪問的記憶體時,會用到交換分割槽(以下簡稱 Swap)。Swap 其實就是把一塊磁碟空間當成記憶體來用。它可以把程式暫時不用的資料儲存到磁碟中(這個過程稱為換出),當程式訪問這些記憶體時,再從磁碟讀取這些資料到記憶體中(這個過程稱為換入)。
不過要注意,通常只在記憶體不足時,才會發生 Swap 交換。並且由於磁碟讀寫的速度遠比記憶體慢,Swap 會導致嚴重的記憶體效能問題。
第三種方式提到的 OOM(Out of Memory),其實是核心的一種保護機制。它監控程式的記憶體使用情況,並且使用 oom_score 為每個程式的記憶體使用情況進行評分:
-
一個程式消耗的記憶體越大,oom_score 就越大;
-
一個程式執行佔用的 CPU 越多,oom_score 就越小。