Linux设备驱动中的阻塞与非阻塞I/O
本文从两个方面进行阐述阻塞与非阻塞、等待队列、轮询机制;
1. 阻塞与非阻塞
1.1 阻塞与非阻塞概念
阻塞:在执行设备操作时,当资源被占用,进程将被挂起处于休眠状态,同时将从调度器的运行队列中移走,直至等待的条件满足时,才用中断来进行唤醒,否则将无法继续执行。
非阻塞:当执行设备操作时,当资源被占用无法获取时,或者立即返回,或者轮询等待,对cpu是一种不断的消耗。
1.2 等待队列
等待队列是用来对阻塞的唤醒机制,同时与进程调度机制相结合,实现异步事件的通知机制。
等待队列的基本操作:
1)定义“等待队列头”
wait_queue_head_t my_queue;
<strong>2) 初始化等待队列头</strong>
init_waitqueue_head(&my_queue);
<strong>3)定义等待队列</strong>
DECLEARE_WAITQUEUE(name, tsk);
<strong>4)添加/删除等待队列</strong>
void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
void fastcall remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
<strong>5)等待事件</strong>
wait_event(queue, condition);
wait_event_interruptible(queue, condition);
wait_event_timeout(queue, condition);
wait_event_interruptible_timeout(queue, condition);
<strong>6)唤醒队列</strong>
与等待队列成对使用
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
<strong>7)等待队列上睡眠</strong>
sleep_on(wait_queue_heat_t *q);
interruptible_sleep_on(wait_queue_head_t *q);
在驱动程序中改变进程状态并调用schedule代码实例:
static ssize_t xxx_write(struct file *file, const char *buffer, size_t count, loff_t *ppos)
{
...
<strong>DECLEARE_WAITQUEUE(wati, current); /*定义等待队列*/
add_wait_queue(&xxx_wait, &wait); /*添加等待队列*/</strong>
ret = count;
/*等待设备缓冲区可写*/
do
{
avail = device_writable(...);
if(avail < 0)
<strong>__set_current_state(TASK_INTERRUPTIBLE); /*改变进程状态*/</strong>
if(avail < 0)
{<strong>
</strong> if(file->f_flags * O_NONBLOC) /*非阻塞*/
{
if(!ret)
ret = - EAGAIN;
goto out;
}
<strong>schedule(); /*调度其它进程*/
if(signal_pending(current) ) /*如果是因为信号量唤醒*/</strong>
{
if(!ret)
{
ret = - ERESTARTSYS;
goto out;
}
}
}
}while (avail < 0)
/*写设备缓冲区*/
device_write(...);
out:
<strong>remove_wait_queue(&xxx_wait, &wait); /*将等待队列移出等待队列头*/
set_current_state(TASK_RUNNING); /*设置进程状态*/</strong>
return ret;
}
1.3 阻塞与非阻塞实例
2. 轮询机制
使用非阻塞IO的应用程序通过使用select()和poll()对设备进行非阻塞的访问。
3. 总结
阻塞与非阻塞是IO操作的两种方式,阻塞通过等待队列来实现,而非阻塞在应用程序层通过select()和poll()来实现,在驱动层通过poll来实现。
4.参考文献
1)Linux设备驱动开发详解 宋宝华著