文章目錄
- ARM架構下Linux中斷處理全流程解析:從硬件觸發到驅動調用 ?
- **一、中斷觸發與硬件層響應** 🔌
- **1. 設備觸發中斷** 📡
- **二、CPU階段:異常入口與上下文處理** 🖥?
- **1. 異常模式切換** 🔄
- **2. 跳轉至中斷向量表** 🗺?
- **三、內核中斷處理框架** ??
- **1. 中斷向量表初始化** 📜
- **2. 中斷控制器驅動注冊** 🔧
- **3. 中斷分發與設備處理** 🔀
- **四、設備驅動中的中斷處理流程** 🛠?
- **1. 驅動注冊中斷處理函數** 📝
- **2. 實現中斷處理函數** 🛠?
- **3. 釋放中斷資源** 🗑?
- **五、示例:網卡中斷處理全流程** 🌐
- **六、關鍵數據結構與機制** 📊
ARM架構下Linux中斷處理全流程解析:從硬件觸發到驅動調用 ?
一、中斷觸發與硬件層響應 🔌
1. 設備觸發中斷 📡
當外設(如網卡、鍵盤)需要CPU處理時,其硬件控制器會通過物理中斷線(IRQ)向中斷控制器發送信號。以ARM的通用中斷控制器(GICv3)為例:
- 中斷接收:GIC Distributor模塊接收中斷請求,并根據中斷類型(SPI/PPI/SGI)分類。
🛠? 關鍵點:SPI用于共享外設中斷,PPI為CPU私有中斷。 - 優先級仲裁:Distributor根據中斷優先級(配置于寄存器
GICD_IPRIORITYRn
)和屏蔽狀態,選擇最高優先級中斷。
?? 優先級規則:數值越小優先級越高,0為最高。 - 路由到目標CPU:通過Redistributor模塊將中斷傳遞給目標CPU核心(支持多核負載均衡)。
🌐 多核優化:避免單核過載,提升系統吞吐量。 - 物理信號觸發:GIC通過CPU的IRQ引腳觸發異常模式切換。
? 信號傳遞:硬件自動完成,無需軟件干預。
二、CPU階段:異常入口與上下文處理 🖥?
1. 異常模式切換 🔄
CPU收到中斷信號后,硬件自動完成以下操作:
- 保存上下文:將當前程序狀態(
PSTATE
、PC
、SP
等)壓入內核棧。
📦 關鍵寄存器:包括通用寄存器、程序計數器、棧指針。 - 切換異常級別:
- 用戶態(EL0)→ 內核態(EL1):觸發完整的上下文切換。
🔒 安全隔離:防止用戶程序直接訪問內核資源。 - 內核態(EL1)→ EL1:僅保存關鍵寄存器,復用當前內核棧。
? 快速路徑:減少模式切換開銷。
- 用戶態(EL0)→ 內核態(EL1):觸發完整的上下文切換。
2. 跳轉至中斷向量表 🗺?
- 向量表基址:由寄存器
VBAR_EL1
指定,指向內核預定義的向量表(arch/arm64/kernel/entry.S
)。
🏷? 配置時機:內核啟動時通過set_vbar()
初始化。 - 入口偏移計算:
- IRQ入口:
VBAR_EL1 + 0x280
(EL1h模式)。
🔍 偏移規則:每種異常類型有固定偏移量。 - 同步異常入口:
VBAR_EL1 + 0x400
(用于系統調用)。
📌 示例:系統調用通過svc
指令觸發同步異常。
- IRQ入口:
// arch/arm64/kernel/entry.S
kernel_ventry 1, irq // EL1h模式IRQ入口
三、內核中斷處理框架 ??
1. 中斷向量表初始化 📜
ARM64的中斷向量表通過匯編宏kernel_ventry
定義,每個條目對應一種異常類型:
- IRQ處理入口:最終調用
handle_arch_irq
(全局函數指針)。
🔗 跳轉邏輯:從匯編跳轉到C語言函數。 - 關鍵匯編跳轉:
irq_handler:bl handle_arch_irq // 跳轉到C語言處理函數
2. 中斷控制器驅動注冊 🔧
以GIC驅動為例,初始化時完成中斷處理函數的綁定:
// drivers/irqchip/irq-gic.c
void __init gic_init(...) {gic_dist_init(gic); // 初始化Distributorgic_cpu_init(gic); // 初始化CPU Interfaceset_handle_irq(gic_handle_irq); // 注冊全局處理函數
}
set_handle_irq
:將gic_handle_irq
賦值給handle_arch_irq
,建立匯編到C的橋梁。
🌉 橋梁作用:屏蔽硬件差異,統一中斷入口。
3. 中斷分發與設備處理 🔀
GIC驅動通過gic_handle_irq
讀取中斷號并分發給設備驅動:
static void __exception_irq_entry gic_handle_irq(...) {u32 irqnr = gic_read_iar(); // 讀取GIC中斷應答寄存器handle_domain_irq(gic_data.domain, irqnr, regs); // 映射并處理
}
// kernel/irq/irqdesc.c
int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,bool lookup, struct pt_regs *regs) {...irq_enter(); // 進入中斷上下文irq = irq_find_mapping(domain, hwirq); // 硬件中斷號映射為虛擬中斷號if (irq合法) {generic_handle_irq(irq); // 調用中斷處理鏈} else {ack_bad_irq(irq); // 錯誤處理}irq_exit(); // 退出中斷上下文...
}
handle_domain_irq
的核心作用:- 中斷上下文標記:
irq_enter()
進入原子上下文,禁用調度。
🚫 禁止行為:禁止睡眠、內存分配等非原子操作。 - 硬件中斷號映射:通過
irq_domain
將硬件IRQ轉換為Linux虛擬IRQ。
🗂? 映射策略:支持線性映射、樹映射等多種方式。 - 調用設備ISR:從
irq_desc[].action
鏈表中執行驅動注冊的中斷處理函數。
? 快速響應:上半部處理時間通常小于1ms。
- 中斷上下文標記:
四、設備驅動中的中斷處理流程 🛠?
1. 驅動注冊中斷處理函數 📝
設備驅動通過request_irq
注冊中斷服務例程(ISR):
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev);
- 參數說明:
irq
:虛擬中斷號(由irq_of_parse_and_map
解析設備樹獲得)。
🌳 設備樹示例:interrupts = <0 168 IRQ_TYPE_EDGE_RISING>; // SPI 168,上升沿觸發
flags
:標志位(如IRQF_SHARED
表示共享中斷)。
?? 共享中斷:需唯一dev_id
標識不同設備。dev
:設備標識符(共享中斷時用于區分設備)。
📌 示例:PCI設備使用pci_dev
指針作為標識。
2. 實現中斷處理函數 🛠?
-
上半部(Top Half):快速響應硬件,禁止阻塞或睡眠。
static irqreturn_t my_irq_handler(int irq, void *dev_id) {struct net_device *dev = dev_id;// 1. 讀取硬件狀態(如網卡DMA緩沖區)u32 status = readl(dev->reg_base + STATUS_REG);// 2. 清除中斷標志writel(STATUS_CLEAR, dev->reg_base + STATUS_REG);// 3. 觸發下半部(如tasklet)tasklet_schedule(&dev->tasklet);return IRQ_HANDLED; }
🚨 注意事項:避免在中斷上下文中調用
kmalloc()
或mutex_lock()
。 -
下半部(Bottom Half):處理耗時任務,支持多種機制:
- SoftIRQ:內核預定義的高優先級任務(如網絡收包)。
🌟 優勢:支持多CPU并行處理。 - Tasklet:基于SoftIRQ,單CPU串行執行。
? 適用場景:GPIO按鍵去抖動處理。 - Workqueue:運行于進程上下文,允許休眠。
🛌 示例:文件I/O或網絡協議棧處理。
- SoftIRQ:內核預定義的高優先級任務(如網絡收包)。
3. 釋放中斷資源 🗑?
驅動卸載時需調用free_irq
釋放中斷號:
void free_irq(unsigned int irq, void *dev_id);
?? 內存安全:必須在驅動卸載路徑中調用,防止資源泄漏。
五、示例:網卡中斷處理全流程 🌐
- 硬件觸發:網卡接收數據包,向GIC發送IRQ信號。
📡 觸發時機:DMA傳輸完成或FIFO緩沖區非空。 - GIC路由:Distributor將中斷路由至CPU0,分配硬件中斷號168。
🔄 負載均衡:GICv3支持動態調整目標CPU。 - CPU跳轉:CPU0執行向量表
VBAR_EL1 + 0x280
處的irq
入口。
?? 低延遲:硬件自動跳轉,無需軟件輪詢。 - GIC處理:
gic_handle_irq
讀取中斷號168,調用handle_domain_irq
。
🔍 中斷號解析:通過GICC_IAR
寄存器獲取。 - 中斷映射:通過
irq_domain
將168映射為Linux虛擬IRQ 200。
🌉 映射關系:存儲在irq_desc[200].irq_data.hwirq
。 - 驅動處理:執行
irq_desc[200].action
中的網卡ISR(如NAPI收包)。
🚀 性能優化:NAPI在收包時切換為輪詢模式,減少中斷風暴。 - 中斷返回:恢復上下文,觸發軟中斷(如
NET_RX_SOFTIRQ
)處理數據。
📦 數據傳遞:sk_buff
從內核空間傳遞到用戶空間。
六、關鍵數據結構與機制 📊
組件/機制 | 功能說明 | 示例/API |
---|---|---|
GIC Distributor | 接收外設中斷,優先級仲裁,路由到目標CPU核心。 | gic_dist_init() |
VBAR_EL1 | 存儲中斷向量表基址,決定異常入口跳轉位置。 | set_vbar() |
irq_domain | 管理硬件中斷號(HW IRQ)到Linux虛擬中斷號(VIRQ)的映射。 | irq_domain_add_linear() |
irq_desc[] | 全局中斷描述符數組,存儲中斷處理函數鏈(action 鏈表)。 | struct irq_desc |
request_irq() | 驅動注冊中斷處理函數,關聯到irq_desc[VIRQ].action 鏈表。 | request_irq() |