全能電路設計實戰

2015年4月20日 星期一

在Raspberry Pi & Pi2 上面學 Linux 驅動程式開發 -- Tasklet --

實驗名稱

    Linux 驅動程式開發 -- Tasklet --

實驗目的

    介紹 tasklet 在 Linux Kernel 裡面的作用和使用方式。

使用材料及設備


    硬體: Raspberry Pi B+

    軟體: 可編譯 Linux Module 的 Raspbian 系統。



原理介紹

    tasklet 就是向系統核心註冊一個工作,系統核心會把該工作排進去它的行程表之中,並等到適當的時機去處理這個被註冊的工作。在核心程式之中遇到 Interrupt (中斷)訊號,需要撰寫 ISR (Interrupt Service Routine,中斷服務程式) 來執行指定中斷發生時要做的事情。 在 ISR 程式執行期料常常會關掉中斷訊號,避免在 ISR 執行尚未結束時又因觸發中斷而重複呼叫相同的 ISR 造成系統的不穩定。

因此 Linux 核心裡面 ISR 要做的事情越少越好,盡量減少執行期間所花的時間。但有時候中斷發生之後要處理的事情很多的時候,通常會讓 ISR 的程式變成只是註冊一個 tasklet 到核心的工作排程裡面,然後就結束 ISR 並打開中斷。 然後讓核心在之後的適當時機再去處理這些較花時間的工作。

舉例來說,Pi 的教育子板上面有按鈕與GPIO 連結,而假設這個時候我們希望寫一個 moudle 使該按鈕被壓下時,去透過網路發送一個訊號 (按鍵式網路 Call 機?) 到指定的網站。處理網站連線的事情往往很浪費時間,而這個時候如果把處理網站連線的事情給寫在 ISR
裡面,很有可能在按鈕快速連按的時候,因為 ISR 尚未結束而造成訊號的漏失。因此我們可以將處理網站連線的事情給寫在一個程式之中。而 ISR 只做把該程式利用 tasklet 的機制給排到 kernel 的核心工作排程之中,留待以後再執行讓 ISR 儘快結束以免漏掉下一次中斷。



程式說明

程式碼內容

---------------- CODE -----------------

---------------- CODE -----------------

程式架構與程式碼說明

#include<linux/interrupt.h>,程式碼的第三行,要使用 tasklet 的排程之前要先引入這個 header 檔。這表示著, tasklet 排程是配合中斷所使用的。為了方便看清楚使用方式,這個範例之中並沒有使用到中斷、ISR 的東西。

最簡單的tasklet的使用流程就是,開始先宣告一個資料結構為 struct tasklet_struct 的變數,並且將該變數和要執行的 function 綁定,並做初始化。之後透過 kernel 提供的API: tasklet_schedule 把這個tasklet 變數給排程到核心的工作排程之中,之後kernel就會找一個適當的時機來執行被綁定的那個 function 。

DECLARE_TASKLET(my_tasklet, my_tasklet_handler, 0); 程式碼的第 8 行,這段程式碼是kernel所提供的巨集用來簡化宣告tasklet 資料結構的步驟。巨集原型為:

DECLARE_TASKLET( name , function , data)

name 是 tasklet 的資料結構變數名稱, function 為該 tasklet 要去執行的函式名稱,而data 為要傳入給function 的參數值。所以在這個範例程式碼第 8 行,my_tasklet 會被宣告成一個 tasklet_struct 的變數,並且和 my_tasklet_handler 這個已在第 7 行就宣告好的 function 綁定在一起。而在程式碼第 10 ~ 15 行則是實作 my_tasklet_handler 的程式碼,要注意一下,這個 function 要傳入一個 unsigned long flag 參數。而傳入的值就是DECLARE_TASKLET 巨集的第三個參數。

tasklet_schedule(&my_tasklet);  程式碼第 21 行。執行完DECLARE_TASKLET巨集完成宣告tasklet的動作之後,下一步就是在適當的時候將其排入系統工作排程之中。而排入工作排程的動作由kernel API: tasklet_schedule(struct tasklet_struct * ptr) 所完成。所以此例的範例 code 中,會在模組被載入的時候馬上將該 tasklet 排入工作排程。

可以看一下範例的 module_init 所執行的程式碼。會先printk 一段訊息之後,再將tasklet 排進 kernel 的工作排程,然後再 printk 一段訊息。藉此觀察程式碼的執行順序與時機。

執行結果

可以看到 my_tasklet 的執行時機會在module_init 執行完之後。


而之後我們將會介紹如何簡單使用另一種工作排程方式 WORK QUEUE 。




沒有留言 :

張貼留言