基礎知識:阻塞與非阻塞
阻塞操作是指在執行裝置操作時若不能獲得資源則掛起程序,直到滿足可操作的條件後再進行操作。被掛起的程序進入休眠狀態,被從排程器的執行佇列移走,直到等待的條件被滿足。而非阻塞操作的程序在不能進行裝置操作時並不掛起,它或者放棄,或者不停地查詢,直至可以進行操作為止。
驅動程式通常需要提供這樣的能力: 當應用程式進行 read()、 write()等系統呼叫時,若裝置的資源不能獲取,而使用者又希望以阻塞的方式訪問裝置,驅動程式應在裝置驅動的 ***_read()、***_write()等操作中將程序阻塞直到資源可以獲取,此後,應用程式的 read()、write()等呼叫才返回,整個過程仍然進行了正確的裝置訪問,使用者並沒有感知到;若使用者以非阻塞的方式訪問裝置檔案,則當裝置資源不可獲取時,裝置驅動的 ***_read()、***_write()等操作應立即返回,read()、write()等系統呼叫也隨即被返回。
阻塞從字面上聽起來似乎意味著低效率,實則不然,如果裝置驅動不阻塞,則使用者想獲取裝置資源只能不停地查詢,這反而會無謂地耗費 cpu 資源。而阻塞訪問時,不能獲取資源的程序將進入休眠,它將 cpu 資源讓給其他程序。
因為阻塞的程序會進入休眠狀態,因此,必須確保有乙個地方能夠喚醒休眠的程序。喚醒程序的地方最大可能發生在中斷裡面,因為硬體資源獲得的同時往往伴隨著乙個中斷。
8.1.1 等待佇列
在 linux 驅動程式中,可以使用等待佇列(waitqueue)來實現阻塞程序的喚醒。wait queue 很早就作為乙個基本的功能單位出現在 linux 核心裡了,它以隊列為基礎資料結構,與程序排程機制緊密結合,能夠用於實現核心中的非同步事件通知機制。等待佇列可以用來同步對系統資源的訪問,第 7 章中所講述的訊號量在核心中也依賴等待佇列來實現。
linux 2.6 提供如下關於等待佇列的操作。
1.定義「等待佇列頭」。
wait_queue_head_t my_queue;
2.初始化「等待佇列頭」。
init_waitqueue_head(&my_queue);
而下面的 declare_w ait_queue_head()巨集可以作為定義並初始化等待佇列頭的「快捷方式」 。
declare_wait_queue_head (name)
3.定義等待佇列。
declare_waitqueue(name, tsk)
該巨集用於定義並初始化乙個名為 name 的等待佇列。
4.新增/移除等待佇列。
void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
add_wait_queue()用於將等待佇列 wait 新增到等待佇列頭 q 指向的等待佇列鍊錶中, 而 remove_wait_queue()用於將等待佇列 wait 從附屬的等待佇列頭 q 指向的等待佇列鍊錶中移除。
5.等待事件。
wait_event(queue, condition)
wait_event_interruptible(queue, condition)
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
第乙個引數 queue 作為等待佇列頭的等待佇列被喚醒,而且第二個引數condition 必須滿足,否則阻塞。wait_event()和 wait_event_interruptible()的區別在於後者可以被訊號打斷,而前者不能。加上_timeout 後的巨集意味著阻塞等待的超時時間,以 jiffy 為單位,在第三個引數的 timeout 到達時,不論 condition 是否滿足,均返回。
wait()的定義如**清單 8.3 所示,從其源**可以看出,當 condition 滿足時,wait_event()會立即返回,否則,阻塞等待 condition 滿足。
6.喚醒佇列
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
上述操作會喚醒以queue作為等待佇列頭的所有等待佇列對應的程序。
wake_up()<--->wait_event()
wait_event_timeout()
wake_up_interruptible()<--->wait_event_interruptible()
wait_event_interruptible_timeout()
wake_up()可以喚醒處於task_interruptible和task_uninterruptible的程序
wake_up_interruptble()只能喚醒處於task_interruptible的程序。
7.在等待佇列上睡眠
sleep_on(wait_queue_head_t *q);
interruptible_sleep_on(wait_queue_head_t *q);
sleep_on()函式的作用就是將當前程序的狀態置成task_uninterruptible,定義乙個等待佇列,並把它新增到等待佇列頭q,直到支援獲得,q引導的等待佇列被喚醒。
interruptible_sleep_on()與sleep_on()函式類似,其作用是將目前程序的狀態置成task_interruptible,並定義乙個等待佇列,之後把它附屬到等待佇列頭q,直到資源可獲得,q引導的等待佇列被喚醒或者程序收到訊號。
sleep_on()<--->wake_up()
interruptible_sleep_on()<--->wake_up_interruptible()
另**
例程1
/*a ****** wait_queue demo
*task_1,task_2 added into the wait_queue, if condition is 0.
*task_3 change condition to 1, and task_1 task_2 will be wake up
*/#include #include #include #include #include #include module_license("gpl");
module_author("[email protected]");
static int condition;
static struct task_struct *task_1;
static struct task_struct *task_2;
static struct task_struct *task_3;
declare_wait_queue_head(wq);
static int thread_func_1(void *data)
return 0;
}static int thread_func_2(void *data)
return 0;
}static int thread_func_3(void *data)
return 0;
}static int __init mod_init(void)
static void __exit mod_exit(void)
int h_init(void)
void h_exit(void)
static int __init hdrv_init(void)
static void __exit hdrv_exit(void)
module_license("gpl");
module_init(hdrv_init);
module_exit(hdrv_exit);
linux裝置驅動開發詳解
第四章 linux核心模組 1.linux核心模組的優點 1 模組可以不用編譯linux核心,在開發除錯的時候,通過動態載入命令載入進核心就可以執行,大大提公升了開發除錯效率,同時也控制了linux核心的大小 2 模組一旦被載入,它就和linux核心其他的部分一樣,直接執行。2.linux核心模組基...
Linux裝置驅動,等待佇列
裝置驅動程式 include include include include include include include module license gpl define buf size 256 define device const char kgrunt struct kgrunt de...
Linux 裝置驅動 核心等待佇列
在 linux 驅動程式設計中,可以使用等待佇列來實現程序的阻塞.等待佇列可以看作儲存程序的容器,在阻塞程序時,將程序放入等待佇列 當喚醒程序時,從等待佇列中取出程序.linux 2.6 核心提供了如下關於等待佇列的操作 wait queue head t my queue init waitque...