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

設(shè)備阻塞IO的實(shí)現(xiàn)

當(dāng)我們讀寫(xiě)設(shè)備文件的IO時(shí),最終會(huì)回調(diào)驅(qū)動(dòng)中相應(yīng)的接口,而這些接口也會(huì)出現(xiàn)在讀寫(xiě)設(shè)備進(jìn)程的進(jìn)程(內(nèi)核)空間中,如果條件不滿足,接口函數(shù)使進(jìn)程進(jìn)入睡眠狀態(tài),即使讀寫(xiě)設(shè)備的用戶進(jìn)程進(jìn)入了睡眠,也就是我們常說(shuō)的發(fā)生了阻塞。In a word,讀寫(xiě)設(shè)備文件阻塞的本質(zhì)是驅(qū)動(dòng)在驅(qū)動(dòng)中實(shí)現(xiàn)對(duì)設(shè)備文件的阻塞,其讀寫(xiě)的流程可概括如下:

1. 定義-初始化等待隊(duì)列頭

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

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

//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-->這個(gè)隊(duì)列用的自旋鎖
--27-->將整個(gè)隊(duì)列"串"在一起的紐帶

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

 55&nbs