在面對驅動程式無法立即滿足請求的情況下,我們需要考慮如何進行回應。通常情況下,呼叫進程並不會關心驅動程式的狀態,所以我們需要在驅動程式中進行對應的處理。一種常見的做法是阻塞該進程的請求。
阻塞I/O是指在執行裝置操作時,如果無法取得所需的資源,就會掛起目前進程,直到滿足可操作的條件後再執行操作。被掛起的進程進入睡眠狀態,並從調度器的運行佇列中移除,直到等待的條件滿足。相反,非阻塞I/O在無法進行裝置操作時,不會掛起進程,而是選擇放棄或持續輪詢,直到可以進行操作為止。
「
#等待佇列是處理阻塞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);
進行初始化操作。
void rkisp1_stream_init(struct rkisp1_device *dev, u32 id) { struct rkisp1_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_t lock; struct list_head task_list; };
init_waitqueue_head()
真正執行的函數是__init_waitqueue_head()
,它的函數定義如下:
void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key) { spin_lock_init(&q->lock); lockdep_set_class_and_name(&q->lock, key, name); INIT_LIST_HEAD(&q->task_list); }
调用DECLARE_WAITQUEUE(wait, current)
将当前进程初始化为等待队列。注意,这里的等待队列和等待队列链表头可不是一个东东。
#define DECLARE_WAITQUEUE(name, tsk) \ wait_queue_t name = __WAITQUEUE_INITIALIZER(name, tsk)
等待队列的定义如下:
struct __wait_queue { unsigned int flags; void *private; wait_queue_func_t func; struct list_head task_list; };
等待队列和等待队列链表头是通过add_wait_queue()
结合到一起的。
init_waitqueue_head(&delay_wait); add_wait_queue(&delay_wait, &wait);
以上代码是将等待队列进程加入到等待队列链表中:
static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new) { list_add(&new->task_list, &head->task_list); }
阻塞进程处理包括两部分内容,首先设置进程的睡眠状态,包括TASK_INTERRUPTIBLE
和TASK_UNINTERRUPTIBLE
两种。前者用于可中断睡眠,后者用于不可中断睡眠。然后,将当前进程退出调度器让出CPU的使用权。
set_current_state(TASK_INTERRUPTIBLE); schedule();
唤醒处理通常位于中断处理函数或某些动作成功执行之后,特定条件满足时,唤醒通过阻塞队列睡眠的进程。例如:
void bitmap_endwrite(struct bitmap *bitmap, sector_t offset, unsigned long sectors, int success, int behind) { if (!bitmap) return; if (behind) { if (atomic_dec_and_test(&bitmap->behind_writes)) wake_up(&bitmap->behind_wait); pr_debug("dec write-behind count %d/%lu\n", atomic_read(&bitmap->behind_writes), bitmap->mddev->bitmap_info.max_write_behind); } ... }
以上是Linux驅動中阻塞IO進程的處理機制的詳細內容。更多資訊請關注PHP中文網其他相關文章!