1. 程式人生 > >linux核心軟中斷和Tasklet機制

linux核心軟中斷和Tasklet機制

 軟中斷

軟中斷的一種典型應用就是所謂的"下半部"(bottom half),它的得名來自於將硬體中斷處理分離成"上半部"和"下半部"兩個階段的機制:上半部在遮蔽中斷的上下文中執行,用於完成關鍵性的處理動作;而下半部則相對來說並不是非常緊急的,通常還是比較耗時的,因此由系統自行安排執行時機,不在中斷服務上下文中執行。bottom half的應用也是激勵核心發展出目前的軟中斷機制的原因。

軟中斷的工作工程模擬了實際的中斷處理過程,當某一軟中斷事件發生後,首先需要設定對應的中斷標記位,觸發中斷事務,然後喚醒守護執行緒去檢測中斷狀態暫存器,如果通過查詢發現有軟中斷事務發生,那麼通過查詢軟中斷向量表呼叫相應的軟中斷服務程式action()。這就是軟中斷的過程,與硬體中斷唯一不同的地方是從中斷標記到中斷服務程式的對映過程。在CPU的硬體中斷髮生之後,CPU需要將硬體中斷請求通過向量表對映成具體的服務程式,這個過程是硬體自動完成的,但是軟中斷不是,其需要守護執行緒去實現這一過程,這也就是軟體模擬的中斷,故稱之為軟中斷。

一個軟中斷不會去搶佔另一個軟中斷,只有硬體中斷才可以搶佔軟中斷,所以硬中斷能夠保證對時間的嚴格要求。

 Tasklet機制

tasklet是linux中斷處理機制中的軟中斷延遲機制。在linux中存在著硬中斷和軟中斷的概念區分。

機制流程:當linux接收到硬體中斷之後,通過tasklet函式來設定軟中斷被執行的優先程度從而導致軟中斷處理函式被優先執行的差異性。

特點:tasklet的優先級別較低,而且中斷處理過程中可以被打斷。但被打斷之後,還能進行自我恢復,斷點續執行。

※ 使用Tasklet

1.宣告自己的tasklet

       既可以靜態地建立tasklet,也可以動態地建立它。如果靜態地建立一個tasklet(直接引用),使用中的兩個巨集:

1.   #define DECLARE_TASKLET(name, func, data) /

2.   struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }

3.    

4.   #define DECLARE_TASKLET_DISABLED(name, func, data) /

5.   struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }

      這兩個巨集都能根據給定的名稱靜態地建立一個tasklet_struct結構。當tasklet被排程以後,給定的函式func會被執行,它的引數由data給出。這兩個巨集的區別在於引用計數器的初始值設定不同。前一個巨集把建立的tasklet的引用計數器設定為0,該taskelet處於啟用狀態,另一則設為1,所以處於禁止狀態。

     還可以通過將一個間接引用(一個指標)賦給一個動態建立的tasklet_struct結構的方式來初始化一個tasklet:

1.   tasklet_init(t,tasklet_handler,dev);      

2.編寫自己的tasklet處理程式

    tasklet處理器程式必須符合規定的函式型別:

    void tasklet_handler(unsigned long data)

    因為是靠軟中斷實現,所以tasklet不能睡眠。這意味著不能在tasklet中使用訊號量或者其他阻塞函式。tasklet執行時執行響應中斷,如果寫的tasklet和中斷處理程式之間共享了某些資料的話,所以要做好預防工作(比如遮蔽中斷後獲取一個鎖)。兩個相同的tasklet絕不會同時執行。

3.排程自己的tasklet

      通過呼叫tasklet_schedule()函式並傳遞給該函式相應的tasklet_struct的指標,該tasklet就會被排程以便執行 :

1.   DECLARE_TASKLET(my_tasklet,my_tasklethandler,dev);

2.    

3.   tasklet_schedule(&my_tasklet);   //把my_tasklet標記為掛起

4.    

      在tasklet被排程以後,只要有機會它就會盡可能早地執行。在它還沒有得到執行機會之前,如果有一個相同的tasklet又被喚醒了,那麼它只會執行一次。而如果此時它已經開始運行了,比如說在另一個處理器上,那麼這個新的tasklet會被重新排程並再次執行,作為優化,一個tasklet總是在排程它的處理器上執行,以更好的利用處理器的高度快取。

tasklet_disable()函式用來禁止某個指定的tasklet。如果該tasklet當前正在執行,這個函式會等到它執行完畢再返回。

      tasklet_disable_nosync()函式也可以用來禁止指定的tasklet,不過它無需在返回前等待tasklet執行完畢。這樣做往往不太安全,因為我們無法估計該tasklet是否仍在執行。

呼叫tasklet_enable()函式可以啟用一個tasklet,要啟用DECLARE_TASKLET_DISABLED()建立的tasklet,也要呼叫這個函式。

通過呼叫tasklet_kill()函式從掛起的佇列中去掉一個tasklet。該函式的引數是一個指向某個tasklet的tasklet_struct的長指標。在處理一個經常重新排程它自身的tasklet的時候,從掛起的佇列中移去已排程的tasklet會很有用。這個函式首先等待該tasklet執行完畢,然後再將它移去。由於該函式可能會引起休眠,所以禁止在中斷上下文中使用它。