全能電路設計實戰

2015年4月30日 星期四

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

實驗名稱:

 -- wait queue --

實驗目的:


    介紹 tasklet 在 Linux Kernel 裡面的作用和原理。

使用材料及設備:


    硬體: Raspberry Pi B+

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


原理介紹:


撰寫模組程式的時候,往往會遇到要因為某項資源還沒有準備好或某個訊息還沒有完成接收的情形,程式就可能會被卡在這裡等待。但是在Linux 模組程式之中如果在這種時候用類似無窮迴圈來做等待的動作的話,會使得系統被卡死而造成系統不穩定。因此系統提供了 wait queue 的機制使模組可以進入等待的狀態並讓 CPU 跑去做其他的事情。而在取得資源或是訊息接收完成時由另外的函式喚醒正在等待狀態中的模組,並且從等待的地方繼續執行下去。

以下示範了一次簡單的 wait queue 註冊與操作流程。在後面的章節中會加入字元裝置的運用,來實作 blocking 和 non-blocking 的 I/O。



程式說明:


程式碼:


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

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

程式架構:


整個程式碼的主要架構也是由一個進入點 ( init_module() ) 和一個離開點 ( cleanup_module() ) 所組成。

該範例最主要的工作都在進入點的地方完成。可以看到掛載模組之後會先安排一個 work queue 去執行一個工作,該工作的內容為在十秒候喚醒正在等待狀態的指定 wait queue。而之後模組會馬上進入wait queue 的等待狀態,並等待十秒後被 work queue 喚醒。喚醒之後則完成並結束 init_module() 的執行。

這樣的情形好似該模組先設定好一個十秒之後會響的鬧鐘( work queue ),然後馬上跑去睡覺 (wait queue) 。十秒後鬧鐘就進行喚醒正在睡覺中的模組。

程式說明


這裡利用到前面章節說明過的 work queue 來進行喚醒等待中的 wait queue。所以可以看到程式碼中宣告了一個 work queue 資料結構 "workq" ,並在模組一開始的進入點被巨集 INIT_WORK() 初始化、和函式 my_workqueue_handler() 綁定在一起。然後馬上被函式schedule_work() 給安排到系統核心排程裡面,而之後核心會安排一個執行緒來執行 my_workqueue_handler()。

而函式 my_workqueue_handler() 裡面除了用函式 printk() 顯示一些訊息幫助了解模組的運行流程,其主要執行的函式為 msleep(10000),作用為在此等待十秒,並在結束等待後執行函式 wake_up_interruptable() 喚醒指定等待中的 wait queue,從程式碼可以知道,被喚醒的 wait queue 名稱為 my_wait_queue。

wait queue 的使用需要用一個特定的資料結構 wait_queue_head_t 來宣告一個變數,並使用函式 init_waitqueue_head() 初始化該變數。

之後當程式需要進入等待狀況的時候,執行函式 interruptible_sleep() 並代入該變數的指標就可以使程式進入等待狀態。 然後使用函式 wake_up_interruptable() 進行喚醒的動作。




沒有留言 :

張貼留言