阻塞型IO(等待队列)

来源:互联网 发布:淘宝网注册开店 编辑:程序博客网 时间:2024/05/17 00:13
如何在驱动程序中实现一个阻塞型IO,等待队列就是最常用的方法。

    对于一个阻塞操作来说,我们的驱动程序应该阻塞该进程,将其置入休眠状态直到请求可继续。

一、休眠的简单介绍

    当一个进程被置为睡眠,它被标识为处于一个特殊的状态并且从调度器的运行队列中去除。直到发生某些情况下改变了这个状态, 进程将才会在任意 CPU 上调度,,即运行该进程。 休眠中的进程会被搁置到系统的一边, 等待将来的某个事件发生。
    
    对Linux驱动来说,让一个进程、进入休眠状态很容易。但是,为了将进程以一种安全的方式进入休眠,我们需要牢记及两条规则:

第一条:
        永远不要在原子上下文中进入休眠。那什么是原子上下文呢:在执行多个步骤时,不能被间断,不能有任何的并发访问。这就意味着,对于休眠来说,我们的驱动程序不能使在持有自旋锁,seqlock或者RCU锁时休眠。如果我们禁止了中断,也不能休眠。
        在持有信号量时休眠是合法的,但是必须仔细检查持有信号量时休眠的那段代码。如果代码在拥有信号量时休眠,任何其他等待该信号量的线程也会休眠,所以任何持有信号量而休眠的代码必须很短。
        最重要要确定持有信号量的休眠不会阻塞最终会唤醒休眠状态的那个进程,否则进入死锁了。
第二条:
        我们永远不知道休眠了多长时间,也不知道休眠期间都发生了些什么事情。很有可能还有其他进程也在同一事件上休眠,并且在我们之前被唤醒并拿走数据,这样,当我们被唤醒后,数据已经被拿走了,仍需继续休眠。所以我们必须检查条件以确保我们扥带的条件为真。

    最后就是我们要确定休眠的进程最终会被某些事件唤醒,否则进程不能休眠,因为不被唤醒,将会一直休眠下去。
    而完成唤醒任务的代码必须能够找到我们的进程,这样才能唤醒休眠的进程。如果同时有多个进程因为读阻塞在休眠状态,而我们要找到要被唤醒的休眠进程,这就意味着需要维护一个队列数据结构。这就是我们在阻塞IO中要用的等待队列,等待队列就是一个进程链表,其中包含了所有等待某个事件的进程。


二、等待队列
        在Linux中,一个等待队列通过一个等待队列头来管理,每当有一个进程发生IO阻塞而发生休眠时就会被添加到相应的等待队列头所管理的等待队列中。这样,当某个事件被唤醒时,等待这个事件的等待队列中所有的进程都会被唤醒,然后根据调度算法,决定哪个进程会先执行。其他进程判断条件不满足后,继续休眠。
    
 1、   等待队列头是一个类型为wait_queue_head_t的结构体,定义在<linux/wait.h>中。可以使用如下方法定义并初始化一个等待队列头:
          DECLARE_WAIT_QUEUE_HEAD(name); 
或者动态地, 如下: 
          wait_queue_head_t my_queue; 
          init_waitqueue_head(&my_queue); 

2、    当一个休眠进程被唤醒后,它并不会立即变为执行态,它必须再次检查它所等待的条件的确为真时,才会变成执行态。Linux中最简单的休眠方式是称为wait_event的宏:
        wait_event(queue, condition) 
        wait_event_interruptible(queue, condition) 
        wait_event_timeout(queue, condition, timeout) 
        wait_event_interruptible_timeout(queue, condition, timeout)

queue 是等待队列头, condition是任意一个表达式,上面的宏在休眠前后都会对表达式求值;如果进程被唤醒后,且condition为真,则结束休眠。

wait_event_interruptible(queue, condition) 这个版本可返回一个整数值,非零值表示休眠被某个信号中断,而驱动程序也需要返回-ERESTARTSYS。为0则表示被正常唤醒。

后面两个带timeout的版本,这里的timeout值表示的是要等待的jiffies值,只要给定时间到期。这两个宏都会返回0。如果由其他时间唤醒,则会返回剩余的延迟实现,并用jiffies表达

3、    当然,既然有休眠就必须有唤醒,其他的某个进程或线程必须为我们执行唤醒操作,否则我们的进程将会一直休眠下去。
用来唤醒休眠进程的基本函数是wake_up,它也有多种形式:
        void wake_up(wait_queue_head_t *queue); 
        void wake_up_interruptible(wait_queue_head_t *queue); 
        wake_up会唤醒给定等待队列头的等待队列中的所有进程,wake_up_interruptible只会唤醒那些执行可中断休眠的进程。

4、我们也可以向等待队列中添加一个成员
    void    fastcall    add_wait_queue( wait_queue_head_t *queue,wait_queue_t   *wait)
        同样,我们也可以型等待队列中移除一个成员
    void    fastcall    add_wait_queue( wait_queue_head_t *queue,wait_queue_t   *wait)

其他:
        不需要判断条件的休眠方式:
        sleep_on(wait_queue_head_t *queue)
        interruptible_sleep_on(wait_queue_head_t *queue)







    
0 0
原创粉丝点击