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

4-1阻塞型IO

2012年08月11日 ⁄ 综合 ⁄ 共 2706字 ⁄ 字号 评论关闭

阻塞型IO

主要内容有:

进程睡眠和唤醒方法

阻塞型I/O的实现方法

Select系统调用的实现方法

 

1、 
休眠的意义

从调度器的运行队列à某个等待队列

直到等到某个事件发生,在从等待队列返回到运行队列。

2、 
如何将进程安全地进入休眠状态

     不能再原子上下文进行休眠

     休眠时,对外界一无所知,进程必修重新检测等待条件

     进程只有确保会被其他进程唤醒,才能进入休眠

3、 
Linux
的休眠机制

进程经常由于某种条件没有得到满足而不得进入睡眠状态,然后等待条件得到满足的时候再继续运行,进入运行状态。这种需求需要等待队列机制的支持。

(等待队列就是一个进程链表,其中包含了等待某个特定时间的所有进程)

4、 
等待队列。

4.1、在Linux驱动程序设计中,可以使用等待队列来实现进程的阻塞,等待队列卡看作是保持进程的容器,在阻塞进程时,将进程放入等待队列,当唤醒进程时,从等待队列中取出进程。

4.2、每个等待任务都会抽象成一个wait_queue,并且挂载到wait_queue_head上。

4.3Linux中等待队列的实现思想:当一个任务需要在某个wait_queue_head上睡眠时,将自己的进程控制块信息分装到wait_queue中,然后挂载到wait_queue_head的链表中,执行调度睡眠。当某些事情发生后,另一个任务(进程)会唤醒wait_queue_head上的某个或者所有任务,唤醒工作也就是将等待队列中的任务设置为可调度的状态,并且从队列中删除。

4.4、等待队列通过“等待队列头”来管理

 
4.4.1
类型:struct wait_queue_head_t

 
4.4.2
定义在<linux/wait.h>

 
4.4.3
定义等待队列 
wait_queue_head_t  xxx_queue

 
4.4.4
初始化等待队列:init_waitqueue_head(&xxx_queue);

 
4.4.5
静态定义并初始化等待队列 
DECLARE_WAIT_QUEUE_HEADxxx_queue;

 4.5等待事件:

 
4.5.1
当进程休眠时,等待被另外进程唤醒,同时检查进程等待的条件是否为真,若为真,则被调度执行;反之继续休眠。

 
4.5.2
等待事件,即为进程等待的条件(condition)。

 4.6休眠函数。

  
wait_event(queue
condition); //queue:等待队列头,通过值传递,
condition
休眠前后都要对该表达式求值;在条件为真之前,进程保持休眠。

  
wait_enent_interruptible(queue,condition);

 

  
wait_event_timeout(queue,condition,timeout);//
等待限定的jiffs时间到达后返回0值,无论condition为何值。

  
wait_envent_interruptible_timeout(queue,condition,timeout);

4.7唤醒函数

  
 wake_up(wait_queue_head_t* queue); //
唤醒所有的等待进程。

   
wake_up_interruptible(wait_queue_head_t* queue);

   

   
wake_up_nr(wait_queue_head_t* queue,int nr); //
唤醒nr个独占等待进程,而不是一个,当nr=0时,唤醒所有的独占等待进程。

   
wake_up_interuptible_nr(wait_queue_head_t* queue,nr);

 

   
wake_up_interuptible_sync(wait_queue_head_t* queue); //
被唤醒后强制调度执行原休眠进程。

4.8实例

  
休眠函数

    

   唤醒函数

    

4.9上面实例中存在一个概率很小的竞态条件。

      
A
B进程都在等在wq的队列上,C进程调用wake_up_interruptible.A进程别唤醒,检查条件flag!=0成立。而此时,调度到B进程,B进程也检查到flag!=0成立。这样,一个事件唤醒了两个进程,可能产生竞态。

4.10可以用原子操作防止这种情况。

4.11设置进程休眠的内部细节

  
4.11.1
分配并初始化一个wait_queue_t结构(包含休眠进程的信息,以及期望被唤醒的相关细节)

  
4.11.2
设置进程的状态,并将其标记为休眠状态(TASK_INTERRUTIBLE、、可中断,TASK_UNINTERRUPTIBLE//不可中断)

      
void set_current_state(int new_state);//
手动设置进程的状态

      
current->state=TASK_INTERRUPTIBLE;//
老内核版本设置方式

  
4.11.3
让出处理器

      
If(
condition)

        
Schedule();//
执行调度,让出处理器。

 
4.12.
手工休眠

 
4.13
唤醒等待队列可能发生的情况

    
4.13.1
当唤醒wake_up时,所有等待在该队列上的进程都被唤醒,并进入可运行状态。

    
4.13.2
如果只有一个进程可获得资源,此时,其他的进程又将再次进入休眠。

    
4.13.3
如果数量很大,被称为“疯狂兽群”

 
4.14
独占等待

   
4.14.1
与普通休眠的不同

         
等待队列入口设置了WQ_FLAG_EXCLUSIVE标志时,则会被添加到等待队列的尾部。而没有这个标志的入口会被添加到头部。

          
在某个等待队列上调用wake_up时,它会在唤醒第一具有WQ_FLAG_EXCLUSIVE的标志的的进程之后停止唤醒其他独占进程。

   
4.14.2
使进程进入独占等待函数:

      
void prepare_to_wait_exclusive(wait_queue_head_t* queue,wait_queue_t *wait,int state);

   
注:使用wait_event的变种读无法使用独占等待。

 

阻塞方式:

5、 
在阻塞型驱动程序中,read实现方式如下:如果进程调用read,但是设备没有数据或数据不足,进程阻塞。当新数据到底后,唤醒被阻塞进程。

6、 
在阻塞型驱动程序中,write实现方式如下:如果进程调用write,但是设备没有足够的空间供其写入数据,进程阻塞。当设备中的数据被读走后,缓冲区中空出部分空间,则唤醒被阻塞进程。

 

阻塞型I/O实例:

    

   

 

  

   声明:本文非原创,整理自申嵌

抱歉!评论已关闭.