马尔代夫度假村有几个:阻塞与非阻塞I/O

来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 21:59:42

阻塞与非阻塞IO

阻塞操作是指在执行设备操作时若不能获得资源,则挂起进程,直到满足可操作的条件后再进行操作。被挂起的进程进入休眠状态,被从调度器的运行队列移走,直到等待条件被满足。而非阻塞操作的进程不能进行设备时并不挂起,它或者放弃,或者不停地查询,直到可以进行操作为止。

阻塞的进程会进入睡眠状态,必须有一个地方能够唤醒休眠的进程。唤醒进程的地方最大可能发生在中断里面,因为硬件资源获得的同时往往伴随一个中断。

 

等待队列

可以使用等待队列(wait queue)来唤醒阻塞进程。wait queue很早就作为一个基本的功能单位出现在linux内核里了,它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步通知机制。等待队列可以用来同步对系统资源的访问,信号量在内核中也依赖等待队列来实现。

1.定义等待队列头

 wait_queue_head my_queue;

2. 初始化等待队列头

init_waitqueue_head(&my_queue);

   DELCARE_WAIT_QUEUE_HEAD(name)/定义并初始化

3. 定义等待队列

DECLARE_WAITQUEUE(name,tsk)

4. 添加/移除等待队列

void fastcall add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);

void fastcall remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);

5. 等待事件

wait_event(queue,condition);

wait_event_interruptible(queue,condition);

wait_event_timeout(queue,timeout);

wait_event_interruptible_timeout(queue,condition,timeout);

 

wait_event()代码:

  #define wait_event(wq,condition)

do {

          if (condition)

                  break;

          __wait_event(wq,condition);//添加到等待队列

} while (0)

 

#define __wait_event(wq,condition)

do {

          DEFINE_WAIT(__wait);

         

          for (;;) {

                  prepare_to_wait(&wq,&__wait,TASK_UNINTERRUPTIBLE);

                  if (condition)

                          break;

                  schedule();//放弃cpu

          }

          finsh_wait(&wq,&__wait);

} while(0)

 

void fastcall prepare_to_wait(wait_queue_head_t *q,wait_queue_t *wait,int state)

{

          unsigned long flags;

 

          wait->flags &= ~WQ_FLAG_EXCLUSIVE;

          sping_lock_irqsave(&q->lock,flags);

          if (list_empty(&wait->task_list))

                  __add_wait_queue(q,wait);//添加等待队列

          if (is_sync_wait(wait))

                        set_current_state(state);//改变当前进程的状态为休眠

                spin_unlock_irqrestore(&q->lock,flags);

}

 

void fastcall finsh_wait(wait_queue_head_t *q,wait_queue_t *wait)

{

        unsigned long flags;

 

        __set_current_state(TASK_RUNNING);//恢复当前进程的状态为TASK_RUNING

        if (!list_empty_careful(&wait->task_list)) {

                spin_lock_irqsave(&q->lock,flags);

                list_del_init(&wait->task_list);

                spin_unlock_irqrestore(&q->lock,flags);

}

 

6. 唤醒队列

void wake_up(wait_queue_head_t *queue);

void wake_up_interruptible(wait_queue_head_t  *queue);

wake_up()唤醒wait_event()或wait_event_timeout(),wake_up_interruptible()或wake_up_interruptible_timeout().wake_up()可唤醒处于TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE的进程,而wake_up_interruptibel()只能唤醒处于TASK_INTERRUPTIBLE的进程。

7.在等待队列上睡眠

sleep_on(wait_queue_head_t *q);

interruptible_sleep_on(wait_queue_head_t *q);

sleep_on()将目前进程的状态置成TASK_UNINTERRUPTIBLE,并定义一个等待队列,之后把它附属到等待队列头q,直到资源可获得,q引导的等待队列被唤醒。

interruptible_sleep_on()与sleep()类似,其作用是将目前进程的状态置成TASK_INTERRUPTIBLE,并定义一个等待队列,之后把它附属到等待队列头q,直到资源可获得的,q引导的等待队列被唤醒或者收到信号。

sleep_on()与wake_up()成对使用,interruptible_sleep_on()与wake_up_interruptible()成对使用。

sleep_on():

void fastcall __sched sleep_on(wait_queue_head_t *q)

{

          SLEEP_ON_VAR

          /*

          #define SLEEP_ONVAR

          unsigned long flags;

          wait_queue_t wait;

          init_waitqueue_entry(&wait,current);*/

          current->state = TASK_UNINTERRUPTIBLE

 

SLEEP_ON_HEAD

/*

#define SLEEP_ONHEAD

spin_lock_irqsave(&q->lock,flags);

__add_wait_queue(q,&wait);

sping_unlock(&q->lock,flags);*/

 

schedule();

SLEEP_ON_TALL

/*

#define SLEEP_ON_TALL

spin_lock_irq(&q->lock);

__remove_wait_queue(q,&wait);*/

spin_unlock_irqrestore(&q->lock,flags);*/

}

 

interruptible_sleep_on():

void fastcall __sched interruptible_sleep_on(wait_queue_head_t *q)

{

          SLEEP_ON_VAR

 

          current->state = TASK_INTERRUPTIBLE;

 

          SLEEP_ON_HEAD

          schedule();

          SLEEP_ON_TALL

}

 

note:在内核中使用set_current_state()或__add_current_queue()函数来实现目前进程状态的改变,直接采用current->state = TASK_INTERRUPTIBLE类似的赋值语句也是可行。通常而言,set_current_state()在任何环境下都可以使用,不会存在并发问题,但是效率要低于__add_current_queue().因此,许多设备驱动并不调用sleep_on()或interruptible_sleep_on(),而是直接进程状态改变和切换。

 

轮询操作

轮询

使用非阻塞I/O的应用程序通常会使用select()和poll()系统调用查询设备是否可对设备进行无阻塞的访问。selecct()和poll()系统调用最终会引发设备驱动中的poll()函数被执行。

应用程序中的轮询编程

应用程序最广泛用到select()系统调用。

int select(int numfds,fd_set *reafds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

struct timeval

{

          int tv_sec;//second

          int tv_usec;//1/1000 second

}

 

FD_ZERO(fd_set *set);//清零set

FD_SET(int fd, fd_set *set);//加fd到set

FD_CLR(int fd, fd_set *set);//清除fd从set

FD_ISSET(int fd, fd_set *set);//判断fd是否被置位

 

设备驱动中的轮询编程

设备驱动中poll()函数:

unsigned int (*poll)(struct file *filp, struct poll_table *watit);

struct poll_table 轮询表。

此函数,对可能引起设备文件状态变化的等待队列调用poo_wait()函数,将对应的等待队列头添加到poll_table; 返回表示是是否能对设备进行无阻塞读写访问的掩码。

poll_wait()注册等待队列:

void poll_wait(struct file *filp, wait_queue_head_t *queue, poll_table *wait);

只是把当前进程加入到wait等待队列表中。

驱动程序poll()返回设备资源可获取状态,即POLLIN,POLLOUT,POLLPRI,POLLERR,POLLNVAL等宏位“或”的结果。

 

阻塞与非阻塞访问是IO访问的两种I/O操作暂时不可进行时会让进程睡眠。

在设备驱动中阻塞IO一般基于等待队列,等待队列可用于同步驱动中事件发生的先后顺序。使用非阻塞IO的应用程序也可借助轮询函数来查询设备是否能立即被访问,用户空间调用select()和poll()接口,设备驱动提供poll()函数,设备驱动的poll()本身不会阻塞,但是poll()和select系统调用则会阻塞地等待文件描述集合中的至少一个可访问或超时。