等待隊列是內核中實現進程調度的一個十分重要的數據結構,其任務是維護一個鏈表,鏈表中每一個節(jié)點都是一個PCB(進程控制塊),內核會將PCB掛在等待隊列中的所有進程都調度為睡眠狀態(tài),直到某個喚醒的條件發(fā)生。應用層的阻塞IO與非阻塞IO的使用我已經在Linux I/O多路復用一文中討論過了,本文主要討論驅動中怎么實現對設備IO的阻塞與非阻塞讀寫。顯然,實現這種與阻塞相關的機制要用到等待隊列機制。本文的內核源碼使用的是3.14.0版本

設備阻塞IO的實現

當我們讀寫設備文件的IO時,最終會回調驅動中相應的接口,而這些接口也會出現在讀寫設備進程的進程(內核)空間中,如果條件不滿足,接口函數使進程進入睡眠狀態(tài),即使讀寫設備的用戶進程進入了睡眠,也就是我們常說的發(fā)生了阻塞。In a word,讀寫設備文件阻塞的本質是驅動在驅動中實現對設備文件的阻塞,其讀寫的流程可概括如下:

1. 定義-初始化等待隊列頭

//定義等待隊列頭wait_queue_head_t waitq_h;//初始化,等待隊列頭init_waitqueue_head(wait_queue_head_t *q); //或//定義并初始化等待隊列頭DECLARE_WAIT_QUEUE_HEAD(waitq_name);

上面的幾條選擇中,最后一種會直接定義并初始化一個等待頭,但是如果在模塊內使用全局變量傳參,用著并不方便,具體用哪種看需求。
我們可以追一下源碼,看一下上面這幾行都干了什么:

//include/linux/wait.h 
 35 struct __wait_queue_head { 
 36         spinlock_t              lock; 37         struct list_head        task_list; 38 }; 39 typedef struct __wait_queue_head wait_queue_head_t;

wait_queue_head_t
--36-->這個隊列用的自旋鎖
--27-->將整個隊列"串"在一起的紐帶

然后我們看一下初始化的宏:

 55&nbs