wait_queue

来源:互联网 发布:学闽南话的软件 编辑:程序博客网 时间:2024/04/28 07:08
/*************************************************************/
引入的数据结构: wait_queue_head and wait_queue
/*************************************************************/
struct __wait_queue_head {
spinlock_t lock;
struct list_headtask_list;
};
typedef struct __wait_queue_head wait_queue_head_t;


struct __wait_queue {
unsigned int flags;
void *private;
wait_queue_func_tfunc;
struct list_headtask_list;
};


/*************************************************************/
wait_queue:解决的是个什么样的问题?
当前进程要等待一个事件的发生:如某个条件成立
把当前进程设置为: interruptible 或者uninterruptible
并通过wait_queue把当前进程 加入到和这个时间相关的wait_queue_head的链表中;


当事件发生时,或者说条件成立时:通过 wait_queue_head找到睡眠在该事件上的进程
并唤醒它;
/*************************************************************/
init_waitqueue_head(&epfile->wait);


wait_event_interruptible(epfile->wait, (ep = epfile->ep));


wake_up(&epfile->wait);


static inline int waitqueue_active(wait_queue_head_t *q)
{
return !list_empty(&q->task_list);
}


/*************************************************************/
以f_fs.c 中一个具体的wait_queue为例:看下使用的接口
操作函数的参数都是: wait_queue_head_t,而不是wait_queue
F_fs.c (drivers\usb\gadget\function):ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
F_fs.c (drivers\usb\gadget\function):init_waitqueue_head(&epfile->wait);
F_fs.c (drivers\usb\gadget\function):waitqueue_active(&epfile->wait));
F_fs.c (drivers\usb\gadget\function):wake_up(&epfile->wait);


/*************************************************************/
#define init_waitqueue_head(q) \
do { \
static struct lock_class_key __key;\
\
__init_waitqueue_head((q), #q, &__key);\
} while (0)




void __init_waitqueue_head(wait_queue_head_t *q, const char *name, struct lock_class_key *key)
{
spin_lock_init(&q->lock);
lockdep_set_class_and_name(&q->lock, key, name);
INIT_LIST_HEAD(&q->task_list);
}




struct ffs_epfile {
wait_queue_head_twait;
}


/*************************************************************/
怎样使当前进程睡眠的?
/*************************************************************/
/
wait_event_interruptible(epfile->wait, (ep = epfile->ep));
/**
 * wait_event_interruptible - sleep until a condition gets true
 * @wq: the waitqueue to wait on
 * @condition: a C expression for the event to wait for
 *
 * The process is put to sleep (TASK_INTERRUPTIBLE) until the
 * @condition evaluates to true or a signal is received.
 * The @condition is checked each time the waitqueue @wq is woken up.
 *
 * wake_up() has to be called after changing any variable that could
 * change the result of the wait condition.
 *
 * The function will return -ERESTARTSYS if it was interrupted by a
 * signal and 0 if @condition evaluated to true.
 */
#define wait_event_interruptible(wq, condition) \
({ \
int __ret = 0;\
might_sleep();\
if (!(condition))\
__ret = __wait_event_interruptible(wq, condition);\
__ret; \
})


#define __wait_event_interruptible(wq, condition) \
___wait_event(wq, condition, TASK_INTERRUPTIBLE, 0, 0,\
     schedule())




#define ___wait_event(wq, condition, state, exclusive, ret, cmd)\
({ \
__label__ __out;\
wait_queue_t __wait;[这才引入了wait_queue_t,用户看到的只是 wait_queue_head]\
long __ret = ret;/* explicit shadow */\
\
INIT_LIST_HEAD(&__wait.task_list);\
if (exclusive)\
__wait.flags = WQ_FLAG_EXCLUSIVE;\
else \
__wait.flags = 0;\
\
for (;;) { \
long __int = prepare_to_wait_event(&wq, &__wait, state);\
\
if (condition)\
break; \
cmd; \
} \
finish_wait(&wq, &__wait);\
__out: __ret; \
})




long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state)
{
unsigned long flags;


if (signal_pending_state(state, current))
return -ERESTARTSYS;


wait->private = current;
wait->func = autoremove_wake_function;


spin_lock_irqsave(&q->lock, flags);
if (list_empty(&wait->task_list)) {//[说的是wait_queue的task_list是empty而不是说的 wait_queue_head]
if (wait->flags & WQ_FLAG_EXCLUSIVE)
__add_wait_queue_tail(q, wait);
else
__add_wait_queue(q, wait);
}
set_current_state(state);
spin_unlock_irqrestore(&q->lock, flags);


return 0;
}


F_fs.c (drivers\usb\gadget\function):wake_up(&epfile->wait);
/*************************************************************/
怎样唤醒睡眠进程?
F_fs.c (drivers\usb\gadget\function):wake_up(&epfile->wait);
/*************************************************************/
#define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL)
void __wake_up(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, void *key)
{
unsigned long flags;


spin_lock_irqsave(&q->lock, flags);
__wake_up_common(q, mode, nr_exclusive, 0, key);
spin_unlock_irqrestore(&q->lock, flags);
}


static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, int wake_flags, void *key)
{
wait_queue_t *curr, *next;


list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
unsigned flags = curr->flags;


if (curr->func(curr, mode, wake_flags, key) &&
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}


int autoremove_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)
{
int ret = default_wake_function(wait, mode, sync, key);


if (ret)
list_del_init(&wait->task_list);
return ret;
}


int default_wake_function(wait_queue_t *curr, unsigned mode, int wake_flags,
 void *key)
{
return try_to_wake_up(curr->private, mode, wake_flags);
}


try_to_wake_up->ttwu_do_activate->ttwu_do_wakeup:p->state = TASK_RUNNING;
static void ttwu_queue(struct task_struct *p, int cpu)
{
struct rq *rq = cpu_rq(cpu);
raw_spin_lock(&rq->lock);
ttwu_do_activate(rq, p, 0);
raw_spin_unlock(&rq->lock);
}
static void
ttwu_do_wakeup(struct rq *rq, struct task_struct *p, int wake_flags)
{
check_preempt_curr(rq, p, wake_flags);
trace_sched_wakeup(p, true);


p->state = TASK_RUNNING;

}


  1. static void check_preempt_curr(struct rq *rq, struct task_struct *p, int flags)  
  2. {  
  3.     const struct sched_class *class;  
  4.   
  5.     if (p->sched_class == rq->curr->sched_class) { //如果rq和p同属一个调度类,那么直接用调用该调度类进行判断就行了。  
  6.         rq->curr->sched_class->check_preempt_curr(rq, p, flags);  
  7.     } else {//否则  
  8.         for_each_class(class) {   
  9.             if (class == rq->curr->sched_class) //这是个很巧妙的实现。for_each_class扫描的顺序是stop_task、rt_task、normal_task、idle_task  
  10.                 break;                      //如果这个条件判断为真,那么说名curr的优先级一定大于p的,那么不能抢占。  
  11.             if (class == p->sched_class) {  
  12.                 resched_task(rq->curr);     //将curr设置为可以抢占的  
  13.                 break;  
  14.             }  
  15.         }  
  16.     }  
  17.   
  18.     /* 
  19.      * A queue event has occurred, and we're going to schedule.  In 
  20.      * this case, we can save a useless back to back clock update. 
  21.      */  
  22.     if (rq->curr->on_rq && test_tsk_need_resched(rq->curr))  
  23.         rq->skip_clock_update = 1;  

1. static void resched_task(struct task_struct *p)

2. {

3.     int cpu;

4.

5.     assert_spin_locked(&task_rq(p)->lock);

6.

7.     if (unlikely(test_tsk_thread_flag(p, TIF_NEED_RESCHED)))

8.         return;

9.

10.      set_tsk_thread_flag(p, TIF_NEED_RESCHED);

11.

12.      cpu task_cpu(p);

13.      if (cpu == smp_processor_id())

14.          return;

15.

16.

17.      smp_mb();

18.      if (!tsk_is_polling(p))

19.          smp_send_reschedule(cpu);

20.  }

设置的当前进程可内核抢占调度
0 0