我们经常看到 read 的时候没有数据可读,而有时却是 write 的时候缓冲区已
满写不进去。而应用程序不关心这个,只关心我要的数据何时能读取出来,或者我
写的数据何时才能写进去。因此这样应该要阻塞进程,直到有数据可读或者有内存
可写。那我们就来看看几个概念和问题:
1)何为进程睡眠?
2)是否什么情况下都可以睡眠?
3)既然睡眠了,如何唤醒?
4)如何操作阻塞I/O?
1、所谓进程睡眠:当一个进程的状态被置为睡眠之后,它将等待运行的队列中
删除,一直到该状态被改变,否则它将不会在任何的 CPU 上运行。简单的说睡眠的
进程将得不到进程调度程序调度,让出 CPU,被搁置到系统的一边。
2、看完了睡眠,那我们也来看看哪些情况不可以睡眠,为什么呢?在<
设备驱动程序>>中,指出:当你运行在原子上下文时不能睡眠。而原子操作包含有
自旋锁、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);
阅读(1111) | 评论(0) | 转发(1) |