cpu 使用率是怎么回事呢?
前言
簡單介紹一下cpu的使用率是怎么來的。
正文
cpu 使用大概有這幾方面:
public class CpuTimeBreakdown
{
// 1. 用戶時間 (User Time)
// - 應用程序在用戶態執行的時間
// - 包括:業務邏輯、算法計算、數據處理
public long UserTime { get; set; }
// 2. 系統時間 (System Time)
// - 應用程序在內核態執行的時間
// - 包括:系統調用、內存管理、進程調度
public long SystemTime { get; set; }
// 3. 空閑時間 (Idle Time)
// - CPU真正空閑的時間
// - 包括:HALT指令、節能模式、等待中斷
public long IdleTime { get; set; }
// 4. I/O等待時間 (I/O Wait Time)
// - CPU等待I/O設備的時間
// - 包括:磁盤I/O、網絡I/O、設備I/O
public long IoWaitTime { get; set; }
// 5. 中斷時間 (IRQ Time)
// - 處理硬件中斷的時間
// - 包括:網絡中斷、磁盤中斷、定時器中斷
public long IrqTime { get; set; }
// 6. 軟中斷時間 (Soft IRQ Time)
// - 處理軟中斷的時間
// - 包括:網絡包處理、定時器處理、調度器中斷
public long SoftIrqTime { get; set; }
// 7. 虛擬化時間 (Steal Time)
// - 被虛擬機監控器占用的時間
public long StealTime { get; set; }
}
然后計算公式如下:
public class CpuUsageCalculator
{
public static CpuUsageBreakdown CalculateCpuUsage(CpuTimeSnapshot current, CpuTimeSnapshot previous)
{
var totalDiff = current.TotalTime - previous.TotalTime;
return new CpuUsageBreakdown
{
// 總CPU使用率 = (總時間 - 空閑時間 - I/O等待時間) / 總時間
TotalUsage = (double)(totalDiff - current.IdleTime + previous.IdleTime -
current.IoWaitTime + previous.IoWaitTime) / totalDiff * 100,
// 用戶態使用率
UserUsage = (double)(current.UserTime - previous.UserTime) / totalDiff * 100,
// 系統態使用率
SystemUsage = (double)(current.SystemTime - previous.SystemTime) / totalDiff * 100,
// 中斷使用率
IrqUsage = (double)(current.IrqTime + current.SoftIrqTime -
previous.IrqTime - previous.SoftIrqTime) / totalDiff * 100,
// I/O等待率
IoWaitUsage = (double)(current.IoWaitTime - previous.IoWaitTime) / totalDiff * 100,
// 空閑率
IdleUsage = (double)(current.IdleTime - previous.IdleTime) / totalDiff * 100
};
}
}
這里就有人問了,cpu 為啥會有空閑時間呢? cpu 應該是一直會運行的,那么這個空閑時間是什么呢?
public class IdleTimeExplanation
{
public static void ExplainIdleTime()
{
Console.WriteLine("CPU空閑時間包括:");
Console.WriteLine("1. HALT指令執行時間 - CPU進入低功耗狀態");
Console.WriteLine("2. 等待中斷的時間 - 沒有可執行的任務");
Console.WriteLine("3. 節能模式時間 - CPU降頻或休眠");
Console.WriteLine("4. 調度器空閑時間 - 沒有就緒進程");
Console.WriteLine("5. 內核空閑循環 - 執行idle進程");
}
}
在操作系統重,會執行idle 進程,不讓cpu空閑下來,這個是為了讓cpu不脫離操作系統的控制。
// Linux內核中的空閑進程
public class IdleProcess
{
public static void IdleLoop()
{
while (true)
{
// 1. 檢查是否有就緒進程
if (HasReadyProcess())
{
ScheduleNextProcess();
break;
}
// 2. 執行HALT指令,進入低功耗狀態
HaltCpu();
// 3. 等待中斷喚醒
WaitForInterrupt();
// 4. 處理中斷
HandleInterrupt();
}
}
private static void HaltCpu()
{
// 執行HLT指令,CPU進入低功耗狀態
// 但CPU仍然可以響應中斷
}
}
那么這里的軟中斷和硬中斷是怎么占用cpu的時間的?
按照我們的思維上來說,處理軟中斷和硬中斷呢? 理論上cpu只是會找到特定的程序的位置,但是處理算是系統處理,應該算在系統上吧。
那么我們看一下定義:系統時間是什么定義:
系統時間是應用程序在內核態執行的時間。
那么這里看到,其實內核調用呢?分為兩個部分一個是系統時間,一個是中斷時間。
public class InterruptVsSystemTime
{
public static void ExplainDifference()
{
Console.WriteLine("中斷時間 vs 系統時間的區別:");
Console.WriteLine();
Console.WriteLine("系統時間 (System Time):");
Console.WriteLine("- 應用程序主動發起的系統調用");
Console.WriteLine("- 進程在用戶態主動調用內核函數");
Console.WriteLine("- 例如:read(), write(), malloc()");
Console.WriteLine("- 進程知道何時開始和結束");
Console.WriteLine();
Console.WriteLine("中斷時間 (IRQ Time):");
Console.WriteLine("- 硬件設備強制中斷當前執行");
Console.WriteLine("- CPU被動響應中斷信號");
Console.WriteLine("- 例如:網卡中斷、磁盤中斷");
Console.WriteLine("- 進程不知道何時發生中斷");
Console.WriteLine("- 中斷處理程序在內核態執行");
}
}
執行的上下文也不同 :
public class ExecutionContext
{
public static void DemonstrateContext()
{
Console.WriteLine("執行上下文對比:");
Console.WriteLine();
Console.WriteLine("系統調用上下文:");
Console.WriteLine("用戶進程 -> 系統調用 -> 內核函數 -> 返回用戶進程");
Console.WriteLine("主動發起,可預測,進程控制");
Console.WriteLine();
Console.WriteLine("中斷處理上下文:");
Console.WriteLine("用戶進程 -> 硬件中斷 -> 中斷處理程序 -> 返回用戶進程");
Console.WriteLine("被動響應,不可預測,硬件控制");
}
}
那么同樣有人會疑問了,硬中斷是硬件中斷的,軟中斷是軟件中斷的? 真的是這個概念嗎? 如果是這樣,系統調用不也是軟中斷嘛?
那么來看一下系統調用和軟中斷的區別:
1.1 基本概念對比
public class SystemCallVsSoftInterrupt
{
public static void ExplainDifference()
{
Console.WriteLine("系統調用 vs 軟中斷:");
Console.WriteLine();
Console.WriteLine("系統調用 (System Call):");
Console.WriteLine("- 用戶程序主動發起的請求");
Console.WriteLine("- 通過特定的指令序列觸發");
Console.WriteLine("- 進程知道何時發生");
Console.WriteLine("- 同步執行");
Console.WriteLine("- 例如:read(), write(), malloc()");
Console.WriteLine();
Console.WriteLine("軟中斷 (Soft IRQ):");
Console.WriteLine("- 內核或硬件觸發的異步事件");
Console.WriteLine("- 通過設置標志位觸發");
Console.WriteLine("- 進程不知道何時發生");
Console.WriteLine("- 異步執行");
Console.WriteLine("- 例如:網絡包處理、定時器處理");
}
}
1.2 觸發機制的不同
; 系統調用的觸發機制
system_call:
; 1. 用戶程序執行syscall指令
syscall
; 2. CPU自動切換到內核態
; 3. 跳轉到系統調用處理程序
; 4. 執行系統調用邏輯
; 5. 返回用戶態
; 軟中斷的觸發機制
soft_irq:
; 1. 內核或硬件設置軟中斷標志
set_bit(SOFTIRQ_BIT, &softirq_pending)
; 2. 在適當時機檢查軟中斷標志
; 3. 執行軟中斷處理程序
; 4. 清除軟中斷標志
軟中斷流程:
public class SoftInterruptFlow
{
public static void DemonstrateSoftInterrupt()
{
Console.WriteLine("軟中斷執行流程:");
Console.WriteLine();
Console.WriteLine("觸發階段:");
Console.WriteLine("1. 硬件中斷處理程序設置軟中斷標志");
Console.WriteLine("2. 硬件中斷處理完成");
Console.WriteLine("3. 返回被中斷的進程");
Console.WriteLine();
Console.WriteLine("執行階段:");
Console.WriteLine("4. 內核在適當時機檢查軟中斷標志");
Console.WriteLine("5. 發現有待處理的軟中斷");
Console.WriteLine("6. 執行軟中斷處理程序");
Console.WriteLine("7. 清除軟中斷標志");
Console.WriteLine();
Console.WriteLine("特點:異步、不可預測、內核控制");
}
}
軟中斷的實現:
// Linux內核軟中斷實現
void do_softirq(void)
{
unsigned long pending;
// 獲取待處理的軟中斷
pending = local_softirq_pending();
if (pending) {
// 執行軟中斷處理程序
if (pending & (1 << NET_RX_SOFTIRQ))
net_rx_action();
if (pending & (1 << TIMER_SOFTIRQ))
run_timer_softirq();
if (pending & (1 << TASKLET_SOFTIRQ))
tasklet_action();
}
}
那么由此可見,并不是說系統調用是軟中斷,軟中斷應該是軟件設置標志位為軟中斷,內核在指定的時機進行處理。
這里我們來復習一下,什么是軟中斷什么是硬中斷:
讓我詳細解釋硬中斷和軟中斷的概念:
一、硬中斷 (Hard IRQ)
public class HardInterrupt
{
public static void ExplainHardInterrupt()
{
Console.WriteLine("硬中斷 (Hard IRQ):");
Console.WriteLine();
Console.WriteLine("定義:");
Console.WriteLine("- 由硬件設備直接發送到CPU的中斷信號");
Console.WriteLine("- CPU必須立即響應的緊急事件");
Console.WriteLine("- 具有最高優先級");
Console.WriteLine("- 可以打斷任何正在執行的代碼");
Console.WriteLine();
Console.WriteLine("特點:");
Console.WriteLine("- 硬件觸發");
Console.WriteLine("- 立即響應");
Console.WriteLine("- 不可屏蔽(某些情況下)");
Console.WriteLine("- 執行時間短");
}
}
1.2 硬中斷的觸發機制
; 硬中斷的處理流程
hardware_interrupt:
; 1. 硬件設備發送中斷信號
; 2. CPU立即停止當前執行
; 3. 保存當前CPU狀態
push rax
push rbx
push rcx
; ... 保存所有寄存器
; 4. 跳轉到中斷處理程序
call interrupt_handler
; 5. 恢復CPU狀態
pop rcx
pop rbx
pop rax
; 6. 返回原任務
iret
1.3 常見的硬中斷
public class CommonHardInterrupts
{
public static void ListHardInterrupts()
{
Console.WriteLine("常見的硬中斷:");
Console.WriteLine();
Console.WriteLine("1. 時鐘中斷 (Timer Interrupt):");
Console.WriteLine(" - 系統時鐘定期發送");
Console.WriteLine(" - 用于時間片調度");
Console.WriteLine(" - 頻率:通常100Hz或1000Hz");
Console.WriteLine();
Console.WriteLine("2. 網絡中斷 (Network Interrupt):");
Console.WriteLine(" - 網卡接收到數據包時發送");
Console.WriteLine(" - 通知CPU有新數據到達");
Console.WriteLine(" - 頻率:取決于網絡流量");
Console.WriteLine();
Console.WriteLine("3. 磁盤中斷 (Disk Interrupt):");
Console.WriteLine(" - 磁盤I/O完成時發送");
Console.WriteLine(" - 通知CPUI/O操作完成");
Console.WriteLine(" - 頻率:取決于磁盤活動");
Console.WriteLine();
Console.WriteLine("4. 鍵盤中斷 (Keyboard Interrupt):");
Console.WriteLine(" - 用戶按鍵時發送");
Console.WriteLine(" - 通知CPU有用戶輸入");
Console.WriteLine(" - 頻率:取決于用戶輸入");
Console.WriteLine();
Console.WriteLine("5. 鼠標中斷 (Mouse Interrupt):");
Console.WriteLine(" - 鼠標移動或點擊時發送");
Console.WriteLine(" - 通知CPU有鼠標事件");
Console.WriteLine(" - 頻率:取決于鼠標活動");
}
}
二、軟中斷 (Soft IRQ)
2.1 軟中斷的定義
public class SoftInterrupt
{
public static void ExplainSoftInterrupt()
{
Console.WriteLine("軟中斷 (Soft IRQ):");
Console.WriteLine();
Console.WriteLine("定義:");
Console.WriteLine("- 由軟件設置的中斷標志");
Console.WriteLine("- 在適當時機由內核處理");
Console.WriteLine("- 優先級低于硬中斷");
Console.WriteLine("- 可以被打斷,但不會被阻塞");
Console.WriteLine();
Console.WriteLine("特點:");
Console.WriteLine("- 軟件觸發");
Console.WriteLine("- 延遲執行");
Console.WriteLine("- 可以屏蔽");
Console.WriteLine("- 執行時間較長");
}
}
對比:
public class InterruptComparison
{
public static void CompareInterrupts()
{
Console.WriteLine("硬中斷 vs 軟中斷對比:");
Console.WriteLine();
Console.WriteLine("觸發方式:");
Console.WriteLine(" 硬中斷:硬件設備直接發送信號");
Console.WriteLine(" 軟中斷:軟件設置標志位");
Console.WriteLine();
Console.WriteLine("響應時間:");
Console.WriteLine(" 硬中斷:立即響應");
Console.WriteLine(" 軟中斷:延遲執行");
Console.WriteLine();
Console.WriteLine("優先級:");
Console.WriteLine(" 硬中斷:最高優先級");
Console.WriteLine(" 軟中斷:較低優先級");
Console.WriteLine();
Console.WriteLine("執行時間:");
Console.WriteLine(" 硬中斷:很短(微秒級)");
Console.WriteLine(" 軟中斷:較長(毫秒級)");
Console.WriteLine();
Console.WriteLine("可屏蔽性:");
Console.WriteLine(" 硬中斷:部分可屏蔽");
Console.WriteLine(" 軟中斷:完全可屏蔽");
}
}
執行上下文對比:
public class ExecutionContext
{
public static void CompareExecutionContext()
{
Console.WriteLine("執行上下文對比:");
Console.WriteLine();
Console.WriteLine("硬中斷上下文:");
Console.WriteLine("- 在中斷上下文中執行");
Console.WriteLine("- 不能睡眠");
Console.WriteLine("- 不能調用可能睡眠的函數");
Console.WriteLine("- 執行時間必須很短");
Console.WriteLine("- 通常只做簡單處理");
Console.WriteLine();
Console.WriteLine("軟中斷上下文:");
Console.WriteLine("- 在軟中斷上下文中執行");
Console.WriteLine("- 不能睡眠");
Console.WriteLine("- 可以執行較復雜的處理");
Console.WriteLine("- 可以批量處理數據");
Console.WriteLine("- 可以調用內核函數");
}
}
軟中斷更像是一種異步處理,一般配合硬中斷, 給一個網絡的例子:
public class NetworkPacketProcessing
{
public static void DemonstrateNetworkProcessing()
{
Console.WriteLine("網絡包處理流程:");
Console.WriteLine();
Console.WriteLine("1. 網卡接收到數據包");
Console.WriteLine("2. 網卡發送硬中斷到CPU");
Console.WriteLine("3. CPU立即停止當前執行");
Console.WriteLine("4. 執行硬中斷處理程序:");
Console.WriteLine(" - 快速讀取數據包");
Console.WriteLine(" - 設置軟中斷標志");
Console.WriteLine(" - 返回原任務");
Console.WriteLine("5. 內核在適當時機檢查軟中斷");
Console.WriteLine("6. 執行軟中斷處理程序:");
Console.WriteLine(" - 批量處理網絡包");
Console.WriteLine(" - 協議棧處理");
Console.WriteLine(" - 路由查找");
Console.WriteLine(" - 轉發或本地處理");
Console.WriteLine();
Console.WriteLine("硬中斷:快速響應,簡單處理");
Console.WriteLine("軟中斷:詳細處理,批量操作");
}
}
偽代碼:
硬中斷:
// 網絡中斷處理示例
public class NetworkInterruptHandler
{
public static void HandleNetworkInterrupt()
{
// 1. 保存CPU狀態(由硬件自動完成)
// 2. 讀取網絡包數據
var packetData = ReadNetworkPacket();
// 3. 解析網絡包
var packet = ParsePacket(packetData);
// 4. 更新網絡統計
UpdateNetworkStats(packet);
// 5. 將包放入處理隊列
EnqueuePacket(packet);
// 6. 恢復CPU狀態(由硬件自動完成)
}
private static byte[] ReadNetworkPacket()
{
// 從網卡緩沖區讀取數據
// 這個過程需要CPU時間
return new byte[1500]; // 典型以太網包大小
}
private static NetworkPacket ParsePacket(byte[] data)
{
// 解析IP頭、TCP頭等
// 這個過程需要CPU時間
return new NetworkPacket();
}
}
軟中斷:
// 網絡軟中斷處理示例
public class NetworkSoftIrqHandler
{
public static void HandleNetworkSoftIrq()
{
// 1. 批量處理網絡包
var packets = GetPendingPackets();
foreach (var packet in packets)
{
// 2. 協議棧處理
ProcessProtocolStack(packet);
// 3. 路由查找
var route = FindRoute(packet);
// 4. 防火墻檢查
if (CheckFirewall(packet))
{
// 5. 轉發或本地處理
ForwardOrDeliver(packet, route);
}
}
// 6. 更新統計信息
UpdateNetworkStats();
}
private static void ProcessProtocolStack(NetworkPacket packet)
{
// 處理IP層
ProcessIPLayer(packet);
// 處理傳輸層(TCP/UDP)
ProcessTransportLayer(packet);
// 處理應用層
ProcessApplicationLayer(packet);
}
}
網絡包的流量大但是情況比較簡單,也就是一個口子讀入,一個口子輸出。
那么磁盤有點不一樣:
public class DiskIoProcessing
{
public static void DemonstrateDiskProcessing()
{
Console.WriteLine("磁盤I/O處理流程:");
Console.WriteLine();
Console.WriteLine("1. 應用程序發起磁盤I/O請求");
Console.WriteLine("2. 內核將請求發送給磁盤控制器");
Console.WriteLine("3. 磁盤控制器執行I/O操作");
Console.WriteLine("4. I/O完成后,磁盤控制器發送硬中斷");
Console.WriteLine("5. CPU執行硬中斷處理程序:");
Console.WriteLine(" - 檢查I/O狀態");
Console.WriteLine(" - 設置軟中斷標志");
Console.WriteLine(" - 返回原任務");
Console.WriteLine("6. 內核執行軟中斷處理程序:");
Console.WriteLine(" - 更新I/O統計");
Console.WriteLine(" - 喚醒等待的進程");
Console.WriteLine(" - 處理下一個I/O請求");
Console.WriteLine();
Console.WriteLine("硬中斷:快速確認I/O完成");
Console.WriteLine("軟中斷:詳細處理I/O結果");
}
}
因為磁盤讀取的時候,可能應用程序發起了對十幾個文件的請求。
那么這個在磁盤可以并發處理的情況下,磁盤的軟中斷只有一位,如何區分是哪個完成了呢?
這個就是對并發軟中斷的處理:
磁盤機制:
public class DiskQueueMechanism
{
public static void ExplainDiskQueue()
{
Console.WriteLine("磁盤隊列機制:");
Console.WriteLine();
Console.WriteLine("1. 磁盤控制器維護命令隊列:");
Console.WriteLine(" - 可以同時接收多個I/O命令");
Console.WriteLine(" - 隊列長度通常為32-256個命令");
Console.WriteLine(" - 支持命令重排序和優化");
Console.WriteLine();
Console.WriteLine("2. CPU可以連續發送多個命令:");
Console.WriteLine(" - 不需要等待前一個命令完成");
Console.WriteLine(" - 磁盤控制器會排隊處理");
Console.WriteLine(" - 支持異步I/O操作");
Console.WriteLine();
Console.WriteLine("3. 磁盤控制器獨立處理:");
Console.WriteLine(" - 磁盤控制器有自己的處理器");
Console.WriteLine(" - 可以并行處理多個命令");
Console.WriteLine(" - 支持命令重排序優化");
}
}
異步I/O機制
public class AsyncIoExample
{
public static async Task DemonstrateAsyncIo()
{
Console.WriteLine("異步I/O示例:");
Console.WriteLine();
// 同時發起多個磁盤I/O操作
var tasks = new List<Task<byte[]>>();
for (int i = 0; i < 10; i++)
{
tasks.Add(File.ReadAllBytesAsync($"file{i}.dat"));
}
// 等待所有I/O操作完成
var results = await Task.WhenAll(tasks);
Console.WriteLine($"同時發起了 {tasks.Count} 個磁盤I/O操作");
Console.WriteLine("CPU不需要等待每個操作完成");
}
}
內核I/O調度器
public class IoScheduler
{
public static void ExplainIoScheduler()
{
Console.WriteLine("內核I/O調度器:");
Console.WriteLine();
Console.WriteLine("1. 請求隊列:");
Console.WriteLine(" - 內核維護I/O請求隊列");
Console.WriteLine(" - 可以同時接收多個請求");
Console.WriteLine(" - 支持請求重排序");
Console.WriteLine();
Console.WriteLine("2. 調度算法:");
Console.WriteLine(" - CFQ (Completely Fair Queuing)");
Console.WriteLine(" - NOOP (No Operation)");
Console.WriteLine(" - Deadline");
Console.WriteLine(" - BFQ (Budget Fair Queuing)");
Console.WriteLine();
Console.WriteLine("3. 并發處理:");
Console.WriteLine(" - 多個進程可以同時發起I/O");
Console.WriteLine(" - 內核會合并和優化請求");
Console.WriteLine(" - 支持異步I/O和I/O多路復用");
}
}
中間通過隊列來完成:
// 磁盤中斷處理程序
irqreturn_t disk_interrupt_handler(int irq, void *dev_id)
{
struct request_queue *q = (struct request_queue *)dev_id;
struct request *req;
// 1. 檢查磁盤狀態
if (disk_operation_completed()) {
// 2. 獲取完成的請求
req = get_completed_request(q);
// 3. 標記請求為完成狀態
req->flags |= REQ_DONE;
// 4. 設置軟中斷標志
raise_softirq(BLOCK_SOFTIRQ);
}
return IRQ_HANDLED;
}
// 塊設備軟中斷處理程序
static void blk_done_softirq(struct softirq_action *h)
{
struct request_queue *q;
struct request *req;
// 遍歷所有請求隊列
list_for_each_entry(q, &all_request_queues, list) {
// 檢查隊列中的每個請求
list_for_each_entry(req, &q->queue_head, queuelist) {
if (req->flags & REQ_DONE) {
// 處理完成的請求
complete_request(req);
}
}
}
}
大概就是這樣一個過程:
public class RequestIdentification
{
public static void ExplainRequestIdentification()
{
Console.WriteLine("請求標識和跟蹤機制:");
Console.WriteLine();
Console.WriteLine("1. 請求標識:");
Console.WriteLine(" - 每個I/O請求都有唯一的ID");
Console.WriteLine(" - 包含設備ID、扇區號、長度等信息");
Console.WriteLine(" - 用于跟蹤請求的狀態");
Console.WriteLine();
Console.WriteLine("2. 請求隊列:");
Console.WriteLine(" - 內核維護請求隊列");
Console.WriteLine(" - 每個設備有獨立的隊列");
Console.WriteLine(" - 隊列中的請求按順序處理");
Console.WriteLine();
Console.WriteLine("3. 完成跟蹤:");
Console.WriteLine(" - 硬中斷處理程序標記完成的請求");
Console.WriteLine(" - 軟中斷處理程序檢查所有請求");
Console.WriteLine(" - 找到標記為完成的請求");
Console.WriteLine(" - 喚醒對應的等待進程");
}
}
結
這里介紹了cpu的使用率是怎么回事,里面介紹了應用時間、系統時間、中斷時間、空閑時間的概念,和他們之間的去唄,更有利于排查cpu高的問題,或者cpu不高,但是系統很慢,了解這些概念后,就容易根據這幾個方面去排查。
浙公網安備 33010602011771號