linux设备驱动中的阻塞与非阻塞I/O
阻塞操作是指在执行设备操作时若不能获得资源则刮起进程,直到满足可操作的条件后再进行操作。
非阻塞操作的进程进行设备操作时并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止。
等待队列
在linux驱动程序中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒。
linux2.6提供如下关于等待队列的操作。
1,定义“等待队列头”
wait_queue_head_t my_queue;
2,初始化“等待队列头”
init_waitqueue_head(&my_queue);
而下面的DECLARE_WAIT_QUEUE_HEAD()宏可以作为定义并初始化等待队列头的快捷方式。
DECLARE_WAIT_QUEUE_HEAD(name)
3,定义等待队列
DECLARE_WAITQUEUE(name,tsk)
该宏用于定义并初始化一个名为name的等待队列。
4,添加/移除等待队列
void fastcall add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);用于将等待队列wait添加到等待队列头q指向的等待
队列链表中。
void fastcall remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);用于将等待队列wait从附属的等待队列头q指向的
等待队列链表中移除。
5,等待事件
wait_event(queue,contion)
wait_event_interruptible(queue,contion)
wait_event_timeout(queue,contion,timeout)
wait_event_interruptible_timeout(queue,contion,timeout)
这4个从函数名字上就可以理解了。
6,唤醒队列
void wake_up(wait_queue_head_t *queue);与wait_event(queue,contion),wait_event_timeout(queue,contion,timeout)配合使
用。可以唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE的进程;
void wake_up_interruptible(wait_queue_head_t *queue);与wait_event_interruptible(queue,contion),
wait_event_interruptible_timeout(queue,contion,timeout)配合使用。只能唤醒处于TASK_INTERRUPTIBLE的进程。
7,在等待队列上睡眠
sleep_on(wait_queue_head_t *queue);将目前进程的状态置成TASK_UNINTERRUPTIBLE,并定义一个等待队列,之后把它附属到等待队
列头q,知道资源可获得,q引导的等待队列被唤醒。
interruptible_sleep_on(wait_queue_head_t *queue);将目前进程的状态置成TASK_INTERRUPTIBLE,并定义一个等待队列,之后把它
附属到等待队列头q,知道资源可获得,q引导的等待队列被唤醒或者进程收到信号。
它们的流程如下所示:
(1)定义并初始化一个等待队列,将进程状态改变为TASK_UNINTERRUPTIBLE或TASK_INTERRUPTIBLE,并将等待队列添加到等待队列头
。
(2)通过schedule()放弃CPU,调度其他进程执行。
(3)进程被其他地方唤醒,将等待队列移除等待队列头。
总结
在设备驱动中阻塞IO一般基于等待队列来实现,等待队列可用于同步驱动中事件发生的先后顺序。使用非阻塞IO的应用程序也可借助
轮询函数来查询设备是否能立即被访问,用户空间调用select()和poll()接口,设备驱动提供poll()函数。设备驱动中的poll()本身
不会阻塞,但是poll()和select()系统调用则会阻塞地等待文件描述符集合中的至少一个可访问或超时。 |