当前位置: > Linux编程 >

简单休眠之等待队列

时间:2014-12-04 19:50来源:linux.it.net.cn 作者:IT
1. 简单休眠
让进程以一种安全的方式进入休眠,必须牢记两条规则:
第一条:永远不能在原子上下文中进入休眠。即是说,我们的进程不能在拥有自旋锁、顺序锁、读写锁时休眠。如果我们已经禁止了中断,也不能休眠。但是,在拥有信号量时休眠是合法的。
第二条:当休眠进程被唤醒时,必须检查以确保我们等待的条件真正为真。
2. 等待队列
Linux中,一个等待队列可以实现简单休眠。一个等待队列由一个”等待队列头”来管理,一个wait_queue_head_t类型的结构,定义在<litlux/wait.h>
一个等待队列头需要定义和初始化后才能使用:
DECLARE_WAIT_QUEUE_HEAD (name)
或者动态地,如下:
wait_queue_head_t my_queue;
init_waitqueue_head(&my_queue);
3. 休眠API
当一个进程休眠,它这样做以期望某些条件在以后会成真。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,是任意的布尔表达式,上面的宏在休眠前后都要求对该表达式求值;在条件为真之前,进程保持睡眠。注意条件可能被任意次地求值,因此它不应当有任何边界效应。
如果你使用wait_event你的进程被置为不可中断地睡眠,它常常不是你所要的首选。最好的选择是wait_event_interruptible,它可能被信号中断,非零值表示休眠被某个信号中断。而你的驱动可能需要返回-ERESTARTSYS。后面的版本((wait_event_timeout和wait_event_interruptible_timeout)只会等待限定的时间;当给定的时间到期后,这两个宏都会返回0值。
4. 唤醒API  
void wake_up (wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue)
wake_up唤醒所有的在给定队列上等待的进程。另一个的形式(wake_up_iterruptible)只会唤醒那些执行可中断休眠的进程。实际上,惯例是:如果你在使用wait_event,就使用wake_up来唤醒;如果你在使用wait_event_interruptible,就是有wake up_interruptible来唤醒。
5. 示例
 typedef struct {
    uint32_t len;
    spinlock_t lock;
    struct list_head list;
    wait_queue_head_t wait;
} bio_queue_t;
typedef struct {
    void * bio;
    struct list_head list;
} bio_t;
bio_queue_t my_queue;


bio_t * get_bio(void)
{
    struct list_head *pos = NULL;
    unsigned long flag = 0;
    for(;;)
    {
        spin_lock_irqsave(&my_queue.lock, flag);
        if(!list_empty(&my_queue.list))
        {
            pos = my_queue.list.next;
            list_del_init(pos);
        }
        spin_unlock_irqrestore(&my_queue.lock, flag);
        if ( NULL == pos)
        {
            wait_event_interruptible(my_queue.wait, (!list_empty(&my_queue.list)));
        }
        else{
            break;
        }
    }
    return (bio_t *)container_of(pos, bio_t, list);
}
void recycle_bio(bio_t * bio)
{
    unsigned long flag = 0;
   
    spin_lock_irqsave(&my_queue.lock, flag);
    list_add_tail(&bio->list, &my_queue.list);
    spin_unlock_irqrestore(&my_queue.lock, flag);
    wake_up_interruptible(&my_queue.wait);
    return ;
}
int init_bio()
{
    bio_t * bio = NULL;
   
    spin_lock_init(&my_queue.lock);
    INIT_LIST_HEAD(&my_queue.list);
    waitqueue_head(&my_queue.wait);
    for(i = 0; i < my_queue.len; i++)
    {
        bio = (bio_t *)kmalloc(sizeof(bio_t), GFP_KERNEL);
        if( NULL == bio)
        {
            return -1;
        }
        list_add_tail(&bio->list, &my_queue.list);
    }
    return 0;
}
 
(责任编辑:IT)
------分隔线----------------------------