內核版本:4.4.189
當驅動程序無法立即滿足請求,該如何響應?而調用進程通常又不會考慮驅動程序的狀態,因此,在驅動程序中就需要做一些處理,而通常的做法是阻塞該進程的請求。
阻塞I/O是指在執行設備操作時,若不能獲得資源,則掛起當前進程直到滿足可操作的條件後再進行操作。被掛起的進程進入到睡眠狀態,從調度器的運行隊列中被移走,直到等待的條件得到滿足。而非阻塞I/O在不能進行設備操作時,並不掛起,它要麼放棄,要麼不停地輪詢,直至可以進行操作為止。
等待隊列是處理阻塞I/O的經典機制。
1. 驅動中阻塞I/O處理流程概況來講,阻塞I/O的處理流程包括4部分內容。首先初始化等待隊列鍊表,該鍊表中存放需要阻塞的進程。然後初始化一個等待隊列,並將當前需要阻塞的進程加入到等待隊列鍊表中。再通過設置進程可中斷狀並將阻塞進程睡眠。最後,當某些條件滿足亦即資源可用時,喚醒等待隊列中的進程。下圖描述了阻塞I/O的處理流程:

在進行初始化等待隊列鍊表之前,我們首先需要定義一個wait_queue_head_t的變量。例如在RK3399的ISP驅動中數據結構struct rkisp1_stream包含了wait_queue_head_t done;。通過調用調用init_waitqueue_head(&stream->done);進行初始化操作。
voidrkisp1_stream_init(structrkisp1_device*dev,u32id){structrkisp1_stream*stream=&dev->stream[id];memset(stream,0,sizeof(*stream));stream->id=id;stream->ispdev=dev;INIT_LIST_HEAD(&stream->buf_queue);init_waitqueue_head(&stream->done);spin_lock_init(&stream->vbq_lock);...}wait_queue_head_t變量的原型是__wait_queue_head,如下所示:
struct__wait_queue_head{spinlock_tlock;structlist_headtask_list;};init_waitqueue_head()真正執行的函數是__init_waitqueue_head(),它的函數定義如下:
void__init_waitqueue_head(wait_queue_head_t*q,constchar*name,structlock_class_key*key){spin_lock_init(&q->lock);lockdep_set_class_and_name(&q->lock,key,name);INIT_LIST_HEAD(&q->task_list);}3. 等待隊列處理調用DECLARE_WAITQUEUE(wait, current)將當前進程初始化為等待隊列。注意,這裡的等待隊列和等待隊列鍊表頭可不是一個東東。
#defineDECLARE_WAITQUEUE(name,tsk)\wait_queue_tname=__WAITQUEUE_INITIALIZER(name,tsk)等待隊列的定義如下:
struct__wait_queue{unsignedintflags;void*private;wait_queue_func_tfunc;structlist_headtask_list;};等待隊列和等待隊列鍊表頭是通過add_wait_queue()結合到一起的。
init_waitqueue_head(&delay_wait);add_wait_queue(&delay_wait,&wait);以上代碼是將等待隊列進程加入到等待隊列鍊表中:
staticinlinevoid__add_wait_queue(wait_queue_head_t*head,wait_queue_t*new){list_add(&new->task_list,&head->task_list);}4. 阻塞進程處理阻塞進程處理包括兩部分內容,首先設置進程的睡眠狀態,包括TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE兩種。前者用於可中斷睡眠,後者用於不可中斷睡眠。然後,將當前進程退出調度器讓出CPU的使用權。
set_current_state(TASK_INTERRUPTIBLE);schedule();5. 喚醒處理喚醒處理通常位於中斷處理函數或某些動作成功執行之後,特定條件滿足時,喚醒通過阻塞隊列睡眠的進程。例如:
voidbitmap_endwrite(structbitmap*bitmap,sector_toffset,unsignedlongsectors,intsuccess,intbehind){if(!bitmap)return;if(behind){if(atomic_dec_and_test(&bitmap->behind_writes))wake_up(&bitmap->behind_wait);pr_debug("decwrite-behindcount%d/%lu\n",atomic_read(&bitmap->behind_writes),bitmap->mddev->bitmap_info.max_write_behind);}...} 我的微信

