IO阻塞和非阻塞模型:
http://blog.csdn.net/sun172270102/article/details/52672917
https://www.cnblogs.com/lubiao/p/4858086.html
等待队列的实现原理实质是利用了内核线程的等待状态属性,也就是说它是一个有睡眠属性的内核线程。
有个bug,驱动在release时候,去撤销线程的时候发现这个线程(内核队列)一直卡在那里即阻塞在那里,导致CPU被消耗,串口也死了,没有反应。
原因查明是因为这个线程里有个while(1)死循环,而这个循环中还有个等待队列。
这个等待队列做了一个超时等待,等待被wakeup,由于这个等待队列没有被唤醒,而超时又时间很长所以导致一直阻塞在这个等待队列里,从而导致
这个线程也被阻塞,线程撤销的时候就没有反应。
note:线程退出时要把该清理的资源清理干净,不然会像这次一样程序死在这里。
另外,在线程里使用while(1)循环时,需要在while(1)里面加上一个msleep()让空出时间片
等待队列只是线程阻塞的一种方式;
等待队列和工作队列一样,都有一个内核缺省的等待对列,同时也可以自己创建一个等待队列
这里有两个概念,一个是等待队列,使用结构体wait_queue_t表示,一个是等待项,使用结构体wait_queue_head_t表示,等待项可以等同于工作队列中task的概念。
等待项需要添加进等待队列,就像task需要添加进工作队列一样,是队列中的成员和队列的关系。
一,使用内核缺省的等待队列
1 wait_queue_head_t my_queue; 定义一个等待项
2, init_waitqueue_head(&my_queue)初始化一个等待项,用这个api初始化的时候已经将这个等待项加入了内核缺省的等待队列
或者直接使用宏DECLARE_WAIT_QUEUE_HEAD(my_queue); 来定义和初始化还有绑定这个等待项到内核缺省的等待队列
3、wait_event(queue, condition) ;(别在中断里面搞)condition为唤醒条件,在一个函数里面等待:
4、wake_up(wait_queue_head_t *queue); 在另一个函数里面唤醒:
二,自己创建一个等待对列
1 wait_queue_t my_wait; 定义一个等待队列
2 init_wait(&my_wait);初始化这个等待队列,或者直接使用宏DEFINE_WAIT(my_wait);来初始化这个等待队列
3 wait_queue_head_t wait_head; 定义一个等待项
4 add_wait_queue()将等待项加入到自己创建的这个等待队列,添加到等待队列中去之后,不会进入睡眠等待
5 remove_wait_queue()从等待队列中移走这个等待项,删除唤醒以后的等待项
6、wait_event(queue, condition) ;(别在中断里面搞)condition为唤醒条件,在一个函数里面等待:
7、wake_up(wait_queue_head_t *queue); 在另一个函数里面唤醒:
常用阻塞接口:
wait_event(wq, condition) 退出阻塞条件为condition为真
wait_event_timeout(wq, condition, timeout) 退出阻塞条件为condition为真或者超时时间到
wait_event_interruptible(wq, condition) 退出阻塞条件为condition为真或者被消息唤醒
wait_event_interruptible_timeout(wq, condition, timeout) 退出阻塞条件为condition为真或者超时时间到或者被其他消息唤醒
常用唤醒接口:
wake_up(wait_queue_head_t *queue)
wake_up_interruptible(wait_queue_head_t *queue)
wake_up_all
如何唤醒等待项:
任何一个wake族函数都会触发一个task,去遍历这个所属的等待队列上的所有睡眠的等待项,然后会去检查每个睡眠进程所关联的wake_up和wait_event中的condition,如果为为真,就将该进程置为TASK_RUNING属性让其继续执行。其他不满足的则继续睡眠。
所以唤醒的条件是:
1.condition 这些条件要置为真
2,使用wake_up族函数去唤醒等待队列
关于wait_event_timeout
http://bbs.chinaunix.net/thread-3615327-1-1.html
do {
DEFINE_WAIT(__wait);
for (;;) { /* break可以退出 */
prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);
if (condition)
break;
ret = schedule_timeout(ret);
if (!ret)/* 若timeout为等完,则进入下一次循环继续等 */
break; /* 若timeout超时,退出 */
}
finish_wait(&wq, &__wait);
schedule_timeout有两种情况退出: 1. wake_up; 2. timer超时;
wake_up会把schedule_timeout打断,此时schedule_timeout注册的定时器并未超时,schedule_timeout直接返回,回到for(;循环,这个循环只有break才能推出,而break有两种情况: 1. 条件为真; 2. ret==0,也就是超时;
如果调用wake_up而condition不为真,则再次调用schedule_timeout,不过此时传入的时间,是上次剩余的时间,当其超时时,wait_event_timeout同样会执行到break退出,所以不会无限的执行下去:
wait_event_timeout - sleep until a condition gets true or a timeout elapses
* @wq: the waitqueue to wait on
* @condition: a C expression for the event to wait for
* @timeout: timeout, in jiffies
*
* The process is put to sleep (TASK_UNINTERRUPTIBLE) until the
* @condition evaluates to true. The @condition is checked each time
* the waitqueue @wq is woken up.
*
* wake_up() has to be called after changing any variable that could
* change the result of the wait condition.