天天看點

linux - 中斷子系統概論

      寫這篇文章的目的,就是對linux中斷子系統的概論,掃盲!後面會以此為專欄,詳細扒代碼去研究中斷子系統。

      剛接觸中斷時,還是在大學階段玩51單片機的時候,最基礎的概念就不用介紹了,中斷的原理及作用這些相信能看到這篇部落格的同學都懂,我就不再贅述!

1.linux對中斷的擴充

     linux将中斷分為:硬體中斷 & 軟體中斷

1.1硬體中斷

       按鍵中斷等硬體産生的中斷,稱之為“硬體中斷”(hard irq)。每個硬體中斷都有對應的處理函數,比如按鍵中斷、網卡中斷的處理函數肯定不一樣。

        為友善了解,你可以先認為對硬體中斷的處理是用數組來實作的,數組裡存放的是函數指針(下圖僅供參考,實際上内部實作比這個複雜許多):

linux - 中斷子系統概論

      當發生A中斷時,對應的irq_function_A函數被調用。硬體導緻該函數被調用。

1.2軟體中斷

      相對應的,還可以人為制造中斷,這就引出了另一個重要的概念:軟體中斷

linux - 中斷子系統概論

      概念有了,就會有疑問:

      Q & A環節:

      Q1:何時發生?

      A1:由軟體決定,對于X号軟體中斷,隻需要将flag_x置1,就表示要發生中斷

      Q2:軟體中斷何時處理?

      A2:軟體中斷的優先級相較于硬體中斷普遍不高,是以隻有在CPU有空再去處理。這個回答肯定會産生一個新的疑問:什麼時候有空?,linux作業系統中,各種硬體中斷發生的很頻繁,至少定時器中斷每10ms發生一次(心跳),是以在處理完硬體中斷後,再去處理軟體中斷

2.中斷的處理原則

2.1原則一:不可嵌套

中斷處理函數需要調用函數,這就需要用到棧。

中斷A正在處理的過程中,假設又發生了中斷B,那麼在棧裡要儲存A的現場,然後處理B。

在處理B的過程中又發生了中斷C,那麼在棧裡要儲存B的現場,然後處理C。

如果中斷嵌套突然暴發,那麼棧将越來越大,棧終将耗盡。

是以,為了防止這種情況發生,也是為了簡單化中斷的處理,在Linux系統上中斷無法嵌套:即目前中斷A沒處理完之前,不會響應另一個中斷B(即使它的優先級更高)。

2.2原則二:越快越好

      為什麼越快越好?這是韋神舉的栗子:

媽媽在家中照顧小孩時,門鈴響起,她開門取快遞:這就是中斷的處理。她取個快遞敢花上半天嗎?不怕小孩出意外嗎?

      在單晶片系統中,例如MCU,假設中斷處理程式冗長,那應用程式在這段時間内就無法執行:系統顯得很遲頓。

     在SMP系統中,假設中斷處理很慢,那麼正在處理這個中斷的CPU上的其他線程也無法執行。

     在中斷的處理過程中,中斷産生的CPU是不能進行排程的,是以中斷的處理要越快越好,盡早讓其他中斷能被處理──程序排程靠定時器中斷來實作。

     在linux系統中使用中斷比較簡單,為某個中斷irq注冊中斷處理函數handler,可以使用request_irq函數:

linux - 中斷子系統概論

     handler就是需要注冊的中斷處理函數,irq_handler_t就是一個傳回值為irqreturn_t類型的函數指針資料類型!

typedef irqreturn_t (*irq_handler_t)(int, void *);      

      但是還存在一個問題,某些中斷要做的事情就是很多,沒辦法加快,例如按鍵中斷,在此期間需要幾十ms的消抖,難道要在handler中等待麼?這對于現今的操作需系統來說,是不可接受的。

3.中斷過程的拆分

     針對2.2中結尾提出的問題,linux中斷系統的維護者們提出了将中斷分為上半部、下半部,按中斷的緊急程度來區分;在handler裡隻處理緊湊的事情(關閉中斷),耗時操作的處理時就打開中斷

linux - 中斷子系統概論

3,1中斷下半部

     中斷下半部的實作有很多方法,概述兩種主要的:小任務&工作隊列

3.1.1小任務(tasklet)

     小任務是處理操作耗時但可以忍受的方法,tasklet的實作是借助軟體中斷實作的,因為是概述,是以隻做原理性介紹,後續需要大量看代碼來梳理。總結一下:

A. 中斷上半部,用來處理緊急的事,需要關閉中斷

B. 中斷下半部,用來處理耗時的、不那麼緊急的事,它是在開中斷的狀态下執行的

C. 中斷下半部執行時,有可能會被多次打斷,有可能會再次發生同一個中斷

D. 中斷上半部執行完後,觸發中斷下半部的處理

E. 中斷上半部、下半部的執行過程中,不能休眠

因為中斷上下文(執行上半部、下半部函數時)切換是,CPU會進入核心空間,這個過程中,硬體的 一些變量和參數也要傳遞給核心,核心通過這些參數進行中斷處理,即所謂的“ 中斷上下文”。在此期間如果讓CPU休眠或者阻塞,将無法退出這種狀态,直接導緻核心僵死!

3.1.2工作隊列

     動作隊列針對操作複雜,耗時時間長的handler。針對這種情況,就不能再用中斷下半部來做,需要使用核心線程來完成這個任務。

     這個線程式系統幫我們建立的,一般是worker線程,核心有很多這樣的線程

在console内使用如下指令:ps -A | grep kworker      

     kworker線程需要在“工作隊列(worke queue)”上去申請一個work,來執行它内部的函數。總結一下流程:

     A.在中斷上半部調用schedule_work(),觸發work處理

4.參考文章