最新消息:XAMPP默认安装之后是很不安全的,我们只需要点击左方菜单的 "安全"选项,按照向导操作即可完成安全设置。

Linux驱动中阻塞IO进程的处理机制

XAMPP案例 admin 146浏览 0评论

当驱动程序无法立即满足请求,该如何响应?而调用进程通常又不会考虑驱动程序的状态,因此,在驱动程序中就需要做一些处理,而通常的做法是阻塞该进程的请求。

阻塞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);进行初始化操作。

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);
 }
...
}

END

转载请注明:XAMPP中文组官网 » Linux驱动中阻塞IO进程的处理机制

您必须 登录 才能发表评论!