中斷處理程序
除了上一章外,迄今為止,我們在內核中所做的每件事都是作為對一個進程請求的回應,要麼通過處理特殊的文件,發送 ioctl,要麼發出系統調用。但是內核的工作並不僅僅是回應進程請求。另一個每個字節都很重要的工作是和連接到機器的硬件對話。
在CPU和計算機的其他設備之間有兩種交互作用。第一種是當CPU對硬件發布命令時,另一種是當硬件要告訴CPU什麼事情時。第二種,被稱為中斷,實現起來是很困難的,因為它不得不處理什麼時間硬件是方便的而不是CPU。典型的硬件設備只有很少的內存,如果當信息可見的時候你不讀取的話它就會消失。
在 Linux 下,硬件中斷被稱為 IRQs [Interrupt Requests (這是Linux起源的Intel 架構上的標准術語。 )的縮寫]。有兩種 IRQs,短的和長的。一個短的 IRQ 預期占用 非常短的一段時間,在那期間,機器的剩余部分被阻塞,沒有其他的中斷將被處理。長的 IRQ 占用的時間長些,在那期間其他中斷有可能發生(但不能是來自同一設備)。只要是可能的,聲明一個長中斷是較好的。
當 CPU 接收到一個中斷,它停止它正在做的任何事情(除非它正在處理一個更重要的中斷,在那種情況下,它將處理完那個中斷後才來處理現在的這個),在堆棧中保存某些參數並調用中斷處理程序。這意味著在中斷處理程序自身中有些東西是不能允許的,因為系統處於一種未知的狀態。解決的辦法是中斷處理程序馬上做完需要做的,通常是從硬件裡面讀什麼或向硬件發送什麼然後安排處理稍後的新信息(這被稱為‘bottom half’)並返回。然後內核保證只要可能就調用bottom half --當這在運行,內核模塊中允許做的所有事情將被允許。
實現這個辦法是當接收到相關的IRQ(在 Intel 平台下有16個)時去調用 request_irq 以使中斷處理程序被調用。 這個函數接收IRQ 號,函數名,標志, /proc/interrupts 中的名字及一個傳送給中斷處理程序的參數作為其參數。標志可以包括 SA_SHIRQ 以指明你願意和其他的中斷處理程序分享那個IRQ(通常因為幾個硬件設備在同一IRQ)以及 SA_INTERRUPT 以指明這是一個快速中斷。這個函數只在那個IRQ上沒有處理程序的情況下成功,或者你願意兩者共享。
然後從中斷處理程序中我們和硬件通信,聯合tq_immediate使用 queue_task_irq 和 mark_bh(BH_IMMEDIATE) 調度 bottom half。我們在 2.0 版中不使用標准的queue_task 的原因是中斷有可能在其他人的 queue_task(queue_task_irq 從這被一個全局鎖保護 -- 在 2.2 版中沒有queue_task_irq 而 queue_task 被一個鎖保護。 )中發生。我們需要 mark_bh 是因為Linux 的早期版本只能有32個 bottom half,而現在它們中的一個(BH_IMMEDIATE) 用於還沒有得到bottom half入口的驅動程序的bottom half連接表。