天天看點

中斷與異常詳解(三)

再次梳理會用到的一些資料結構和名詞

中斷向量表(中斷描述符表) idt_table 全局,8位元組64位,從低到高位16位段選擇符,32位偏移量,16位狀态資訊 256項

起始位址在核心資料節的idt中

用于尋找各種門,門的作用是防止使用者程式通路陷阱門、中斷門等特殊資源,出于安全考慮,linux為使用者留有3,4,5,128号系統調用門供使用者使用

中斷描述符表寄存器 IDTR 寄存器,6位元組48位,低16位界限,高32位基址 1項 用于快速尋找中斷描述符,linux定義的16位界限為8*256-1,即使用了2048位元組來存儲中斷向量表;32位基址就是idt位址,整個48位内容在記憶體的位置為idt位址-2
中斷服務程式數組 Irq_desc 全局,20位元組160位,5個unsigned int 224項 中斷線狀态status,中斷控制器描述符hw_irq_controller和中斷服務例程連結清單irqaction,中斷嵌套depth和多cpu鎖lock,通過這些資訊可以對中斷線進行管理
中斷服務例程描述符 irqaction 結構體,24位元組,6個32位 用于挂載到中斷線服務例程連結清單上的結構體,真正的對中斷進行個性化處理,也是裝置驅動程式主要實作内容
現場寄存器結構 pt_regs 函數參數,60位元組,15個32位 通常作為中斷或異常處理程式的參數,這些函數前面通常有asmlinkage,通過棧傳參數,即第一個參數就是ESP

《深入分析linux核心》一書74頁對request_irq()函數解釋為“将對應的中斷服務例程挂入中斷請求隊列”,我産生了一些誤解,個人認為解釋為“請求将中斷服務例程挂入中斷服務隊列(單連結清單)”更好了解一點,因為核心初始化挂載中斷服務例程都是直接操作斷服務例程的指針的,服務例程在核心而且肯定存在記憶體,是以不用管這些,但其他裝置初始化的時候,挂載操作就需要嚴格要求,比如全局唯一div_id,中斷号大小,申請空間存儲中斷服務例程指針等都需要确認,是以需要先請求,這也就是對挂載操作封裝一層request_irq()的原因吧,是以隻留一個中斷服務隊列的說法,請求隊列容易誤解。

接下來就說說do_irq()

asmlinkage unsigned int do_IRQ(struct pt_regs regs)

{        

int irq = regs.orig_eax & 0xff; 因為之前壓棧壓的irq-256,是以這裡需要& 0xff來恢複,真是神奇,&可以這樣用,irq在0-223之間
int cpu = smp_processor_id(); 擷取cpu号
irq_desc_t *desc = irq_desc + irq; 擷取中斷服務描述符(中斷服務程式)的入口,不是中斷服務例程描述符哦
struct irqaction * action;
unsigned int status;
kstat.irqs[cpu][irq]++; 記錄中斷請求次數
spin_lock(&desc->lock); 鎖cpu,中斷設計成單cpu不可重入,因而一條中斷線在一個時間點隻能由一個cpu來處理
desc->handler->ack(irq); 對中斷請求給予确認,也不知道确認什麼,8259A控制器裡面都沒這個函數。。
status = desc->status & ~(IRQ_REPLAY | IRQ_WAITING); 設定狀态為未删除,非探測,因為是由硬體真正發出的中斷請求
status |= IRQ_PENDING;  接受中斷,并準備處理
action = NULL; 中斷服務例程隊列(單連結清單)初始為空
if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { 如果不是中斷線被禁用或者有中斷服務例程正在處理
          action = desc->action; 獲得中斷服務例程隊列
          status &= ~IRQ_PENDING; 送出處理
          status |= IRQ_INPROGRESS; 開始處理
}
desc->status = status; 更新中斷線狀态
if (!action) 如果擷取中斷服務隊列失敗
         goto out; 退出
for (;;) { 循環
           spin_unlock(&desc->lock); 釋放多cpu鎖,但此時中斷線狀态是IRQ_INPROGRESS,是以即使其他cpu無法擷取目前中斷線的中斷服務例程隊列,而且此時應該是關中斷狀态吧,這裡有些疑問
          handle_IRQ_event(irq, &regs, action); 中斷服務例程隊列對中斷進行處理,就是讓隊列裡的每個例程都去試着處理一下
          spin_lock(&desc->lock); 擷取多cpu鎖,由于上面的中斷服務例程處理會先關中斷,然後再開中斷,就有可能在關中斷後擷取鎖之前再發生一次中斷,也就是中斷嵌套,雖然這應該盡量避免。但不了解的是即使這條中斷線再次發生中斷,也因為status為IRQ_INPROCESS而無法設定status為IRQ_PENDING啊,難道是怕驅動程式設定這個status?反正作業系統為我們考慮很全面了,有嵌套也能在這兒串行執行
         if (!(desc->status & IRQ_PENDING)) 再檢測中斷線是否有中斷請求,如果沒有嵌套。更新:既然上面說了這條中斷線可能又來一個中斷,那麼就有可能被其他cpu甚至自己捕獲并設定了IRQ_PENDING狀态,但因為目前為IRQ_INPROCESS狀态,這次新的中斷請求時無法被他們處理的,是以需要再次處理
                     break; 退出
         desc->status &= ~IRQ_PENDING; 送出請求
} 再次進行中斷
desc->status &= ~IRQ_INPROGRESS; 恢複中斷線為沒有中斷例程正在服務狀态

out:

desc->handler->end(irq); 不明,根據标志位是否啟用中斷線吧?
spin_unlock(&desc->lock); 釋放多cpu鎖
if (softirq_pending(cpu)) 不明,64位cpu才有的資料結構
       do_softirq(); 處理軟中斷
return 1;

}

轉載于:https://www.cnblogs.com/hmxb/p/4904758.html

繼續閱讀