现在的位置: 首页 > 综合 > 正文

字符驱动之四阻塞IO(等待队列)

2013年08月22日 ⁄ 综合 ⁄ 共 2580字 ⁄ 字号 评论关闭
我们经常看到 read 的时候没有数据可读,而有时却是 write 的时候缓冲区已
 
满写不进去。而应用程序不关心这个,只关心我要的数据何时能读取出来,或者我
 
写的数据何时才能写进去。因此这样应该要阻塞进程,直到有数据可读或者有内存
 
可写。那我们就来看看几个概念和问题:
 
    1)何为进程睡眠?
   
    2)是否什么情况下都可以睡眠?
 
    3)既然睡眠了,如何唤醒?
 
    4)如何操作阻塞I/O?
   
    1、所谓进程睡眠:当一个进程的状态被置为睡眠之后,它将等待运行的队列中
 
除,一直到该状态被改变,否则它将不会在任何的 CPU 上运行。简单的说睡眠的
 
程将得不到进程调度程序调度,让出 CPU,被搁置到系统的一边。
 
    2、看完了睡眠,那我们也来看看哪些情况不可以睡眠,为什么呢?在<<linux
 
备驱动程序>>中,指出:当你运行在原子上下文时不能睡眠。而原子操作包含有
 
旋锁、BKL、RCU、seqlock、中断上下文。到了这里,大家有个疑惑,那信号量为
 
可以睡眠呢?那我们先来看看原子操作的概念吧,从字面来看,它应该采用了物
 
学的物质微粒的概念,应该是不可再分的操作啊==>所谓原子操作,就是该操作绝
 
会在执行完毕前被任何其他任务或事件打断(连中断也不行吗?),也就说,它的
 
小的执行单位,不可能有比它更小的执行单位。在单核 CPU 中,原子操作至少保
 
了两点:<在SMP中,我不是很清楚,就不多做解释了,若有人知道,可告知哦>
 
    1)原子操作是最小的执行单位,一口气执行完毕,任何打断都无效;
    
    2)保护了共享资源的完整性,不被破坏。
 
    自旋锁禁止CPU抢占,而信号量允许CPU抢占。在单 CPU 情况下,若持有自旋锁
 
进程进入睡眠,由于禁止了CPU抢占,其他进程将永远得不到CPU执行,将唤不醒
 
自旋锁,你说那系统岂不进入睡眠状态啊。那中断呢?能否唤醒自旋锁睡眠呢?一
 
般而言,中断程序能正常运行,但在中断程序中几乎不会做唤醒自旋锁的可能(除
 
非专门设计),所以系统也将处于睡眠状态。而对SMP就不同了,因为各自的CPU执行
 
各自的程序,而自旋锁只是禁止自己的CPU抢占而又当前睡眠而已,所以SMP可以唤
 
醒自旋锁,但是这样做,还是比较危险的啊!!而信号就不同了,它允许CPU抢占。
 
    中断一般操作就是进入中断后,就把关掉中断,直到中断结束再重启中断。若
 
我们关了本地中断后睡眠,任何一个操作中断IO的进程都会睡眠,一旦唤醒之后,
 
多个进程为了抢占资源而并发,从而会导致共享数据的破坏,更有甚者将造成进程
 
死锁啊。
 
     到了这里,应该知道了为何原子操作不能睡眠了吧,两方面:一方面不要进程
 
睡眠,结果导致了系统睡眠或造成死锁,另一方面是保护共享资源。
 
     3、有了睡眠,当然就要有唤醒啦。要不睡眠将毫无意义,因为睡眠只是为了
 
等候到自己所要的东西,取到东西必须返回啊。比如说:你等到的有数据,你一高
 
兴就屁颠屁颠的跑了,哪里还会在那里傻等啊;同时你好不容易等到有内存写进去
 
啦,当然马上写入,然后就跑出去玩了。
 
     4、睡眠IO操作方法: 
       
     使用睡眠IO需要考虑的三个问题:什么情况需要睡眠?什么情况得唤醒?
 
何要睡眠?
 
#include <linux/wait.h>
第一步:【定义等待队列头并初始化】
/* <动态申请等待队列>
 * @brief             初始化等待队列头
 * @param[in]        queue 我们所要等待的队列
 * @return            no
 */
//wait_queue_head_t my_queue;                    /*定义等待队列*/
init_waitqueue_head(wait_queue_head_t *queue);   /*初始化*/
/*<静态申请等待队列>
 * @brief             初始化等待队列头
 * @param[in]        queue 我们所要等待的队列
 * @return            no
 */
DECLARE_WAIT_QUEUE_HEAD(wait_queue_head_t *queue);

第二步:【有条件的睡眠】
[NOTE]看你要选择哪种睡眠方式,还有一种无条件睡眠方式这里没介绍
/*
 * @brief     不可中断的睡眠
 * @param[in] queue 我们所要等待的队列《注意它是"通过值"传递的》
 * @param[in] condition 条件是一任意的布尔表达式;它不应当有任何边界效应.
 * @return    添加成功返回 0;      
 */
wait_event(queue, condition)

/*
 * @brief             可中断的睡眠
 * @param[in]        queue 我们所要等待的队列《注意它是"通过值"传递的》
 * @param[in] condition 条件是一任意的布尔表达式;它不应当有任何边界效应.
 * @param[in] timeout 等待一段有限的时间后睡眠
 * @return        
 */
wait_event_timeout(queue, condition, timeout)

/*
 * @brief     可中断的睡眠
 * @param[in] queue 我们所要等待的队列《注意它是"通过值"传递的》
 * @param[in] condition 条件是一任意的布尔表达式;它不应当有任何边界效应.
 * @return    添加成功返回0;若中断返回-ERESTARTSYS         
 */
【说明】它被唤醒不单单是condition条件为真的时候,还有中断的时候。
wait_event_interruptible(queue, condition)

wait_event_interruptible_timeout(queue, condition, timeout)

第三步:【睡眠的唤醒】
/*
 * @brief             唤醒给定队列的所有进程
 * @param[in]        queue 我们所要等待的队列《注意它是"通过值"传递的》
 * @return            no
 */
【注意】若唤醒多个进程,则会产生竞态。Q:要如何进行处理呢? 
void wake_up(wait_queue_head_t *queue);

/*
 * @brief             唤醒给定队列的可中断的所有进程
 * @param[in]        queue 我们所要等待的队列《注意它是"通过值"传递的》
 * @return            no
 */

void wake_up_interruptible(wait_queue_head_t *queue);

【上篇】
【下篇】

抱歉!评论已关闭.