close

內核版本:4.4.189

當驅動程序無法立即滿足請求,該如何響應?而調用進程通常又不會考慮驅動程序的狀態,因此,在驅動程序中就需要做一些處理,而通常的做法是阻塞該進程的請求。

阻塞I/O是指在執行設備操作時,若不能獲得資源,則掛起當前進程直到滿足可操作的條件後再進行操作。被掛起的進程進入到睡眠狀態,從調度器的運行隊列中被移走,直到等待的條件得到滿足。而非阻塞I/O在不能進行設備操作時,並不掛起,它要麼放棄,要麼不停地輪詢,直至可以進行操作為止。

等待隊列是處理阻塞I/O的經典機制。

1. 驅動中阻塞I/O處理流程

概況來講,阻塞I/O的處理流程包括4部分內容。首先初始化等待隊列鍊表,該鍊表中存放需要阻塞的進程。然後初始化一個等待隊列,並將當前需要阻塞的進程加入到等待隊列鍊表中。再通過設置進程可中斷狀並將阻塞進程睡眠。最後,當某些條件滿足亦即資源可用時,喚醒等待隊列中的進程。下圖描述了阻塞I/O的處理流程:

2. 初始化等待隊列鍊表

在進行初始化等待隊列鍊表之前,我們首先需要定義一個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);}...}

END

我的微信

--- 往期精彩內容 ---
內核圖顯子系統專輯內核驅動原理專輯內核驅動調試專輯內核及32/64位ARM處理器工作原理專輯
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 鑽石舞台 的頭像
    鑽石舞台

    鑽石舞台

    鑽石舞台 發表在 痞客邦 留言(0) 人氣()