首頁  >  文章  >  系統教程  >  Linux驅動中阻塞IO進程的處理機制

Linux驅動中阻塞IO進程的處理機制

王林
王林轉載
2024-02-11 16:45:03581瀏覽

在面對驅動程式無法立即滿足請求的情況下,我們需要考慮如何進行回應。通常情況下,呼叫進程並不會關心驅動程式的狀態,所以我們需要在驅動程式中進行對應的處理。一種常見的做法是阻塞該進程的請求。

阻塞I/O是指在執行裝置操作時,如果無法取得所需的資源,就會掛起目前進程,直到滿足可操作的條件後再執行操作。被掛起的進程進入睡眠狀態,並從調度器的運行佇列中移除,直到等待的條件滿足。相反,非阻塞I/O在無法進行裝置操作時,不會掛起進程,而是選擇放棄或持續輪詢,直到可以進行操作為止。

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

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

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

Linux驅動中阻塞IO進程的處理機制

2. 初始化等待佇列鍊錶

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

3. 等待队列处理

调用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);
}

4. 阻塞进程处理

阻塞进程处理包括两部分内容,首先设置进程的睡眠状态,包括TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE两种。前者用于可中断睡眠,后者用于不可中断睡眠。然后,将当前进程退出调度器让出CPU的使用权。

set_current_state(TASK_INTERRUPTIBLE);
schedule();

5. 唤醒处理

唤醒处理通常位于中断处理函数或某些动作成功执行之后,特定条件满足时,唤醒通过阻塞队列睡眠的进程。例如:

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中文網其他相關文章!

陳述:
本文轉載於:lxlinux.net。如有侵權,請聯絡admin@php.cn刪除