1.阻塞与非阻塞I/O
阻塞操作:在执行设备操作时,若不能获得资源,则挂起进程直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待的条件被满足
非阻塞操作:在不能进行设备操作时,并不挂起,它或者放弃,或者不停地查询,直至可以进行操作为止
阻塞能使效率高:如果设备驱动不阻塞,则用户想获取设备资源时只能不停地查询,这反而会无谓地耗费CPU资源;而阻塞访问时,不能获取资源的进程将进入休眠,它将CPU资源“礼让”给其他进程
因为阻塞的进程会进入休眠状态,因此,必须确保有一个地方能够唤醒休眠的进程,否则,进程就真的“寿终正寝”了。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随着一个中断
2.等待队列
在LINUX驱动程序中,可以使用等待队列(wait queue)来实现阻塞进程的唤醒,等待队列可以看作保存进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,从等待队列中取出进程
LINUX2.6提供如下关于等待队列的操作:
1.定义“等待队列头”
wait_queue_head_t my_queue;
2.初始化“等待队列头”
init_waitqueue_head(&my_queue);
定义并初始化“等待队列头”的快捷方式(综合1、2步):
DECLARE_WAIT_QUEUE_HEAD(my_queue);
3.添加/移除等待队列
add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
remove_wait_queue(wait_queue_head_t *q, wait_queue_t *wait);
add_wait_queue()用于将等待队列wait添加到等待队列q指向的等待队列链表中
4.等待事件
wait_event(queue, condition);
wait_event_interruptible(queue, condition);
wait_event_timeout(queue, condition, timeout);
wait_event_interruptible _timeout(queue, condition, timeout);
等待第一个参数queue作为等待队列头的等待队列被唤醒,而且第2个参数condition(bool表达式)必须满足,否则继续阻塞。wait_event()和
wait_event_interruptible()的区别在于后者能被信号打断,而前者不能。加上timeout后的宏意味着阻塞等待的超时时间,以jiffy为单位,在第3个参数timeout到达时,不论condition是否满足,均返回
5.唤醒队列
void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);
wake_up()从等待队列queue中唤醒状态为TASK_UNINTERRUPTIBLE和
TASK_INTERRUPTIBLE的进程,而wake_up_interruptible()只能唤醒处于
TASK_INTERRUPTIBLE的进程
6.在等待队列上睡眠
sleep_on(wait_queue_head_t *q);
interruptible_sleep_on(wait_queue_head_t *q);
sleep_on()函数的作用就是将目前进程的状态置成TASK_UNINTERRUPTIBLE,并定义一个等待队列,之后把它附属到等待队列头q,直到资源可获得,q引导的等待队列被唤醒