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) |