您的位置:首页 > 产品设计 > UI/UE

wait queue 2

2017-01-14 17:40 281 查看
除了使用DEFINE_WAIT 来定义wait queue后,还可以使用init_waitqueue_entry 来初始化wait queue

static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)

{

    q->flags    = 0;

    q->private    = p;

    q->func        = default_wake_function;

}

这两者的却别是DEFINE_WAIT 的回调函数是

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;

}

这个函数除了调用default_wake_function外,还会调用list_del_init来将wait->task_list 的所有list删掉,并重新初始化.

static inline void list_del_init(struct list_head *entry)

{

    __list_del_entry(entry);

    INIT_LIST_HEAD(entry);

}

还可以调用init_wait_entry 来初始化,这个就和DEFINE_WAIT一模一样了.

void init_wait_entry(wait_queue_t *wait, int flags)

{

    wait->flags = flags;

    wait->private = current;

    wait->func = autoremove_wake_function;

    INIT_LIST_HEAD(&wait->task_list);

}

如果wakeup 后的task还要满足某个条件才能执行的话,就不能直接调用prepare_to_wait 。而应调用wait_event

#define wait_event(wq, condition)                    \

do {                                    \

    might_sleep();                            \

    if (condition)                            \

        break;                            \

    __wait_event(wq, condition);                    \

} while (0)

我们再来看看__wait_event的实现

#define __wait_event(wq, condition)                    \

    (void)___wait_event(wq, condition, TASK_UNINTERRUPTIBLE, 0, 0,    \

                schedule())

#define ___wait_event(wq, condition, state, exclusive, ret, cmd)    \

({                                    \

    __label__ __out;                        \

    wait_queue_t __wait;                        \

    long __ret = ret;    /* explicit shadow */            \

                                    \

    init_wait_entry(&__wait, exclusive ? WQ_FLAG_EXCLUSIVE : 0);    \

    for (;;) {                            \

        long __int = prepare_to_wait_event(&wq, &__wait, state);\

                                    \

        if (condition)                        \

            break;                        \

                                    \

        if (___wait_is_interruptible(state) && __int) {        \

            __ret = __int;                    \

            goto __out;                    \

        }                            \

                                    \

        cmd;                            \

    }                                \

    finish_wait(&wq, &__wait);                    \

__out:    __ret;                                \

})

注意cmd就是schedule()

这个___wait_event 首先调用init_wait_entry 来初始化wait queu,也就是说使用wait_event的话,前面调用wait queue的初始化过程都可以省了.

由于这个条件可能wakup好多次只有才能满足,因此这里用了for的死循环。

然后会调用prepare_to_wait_event 将wait queue加入到wait_queue_head_t 中

long prepare_to_wait_event(wait_queue_head_t *q, wait_queue_t *wait, int state)

{

    unsigned long flags;

    long ret = 0;

    spin_lock_irqsave(&q->lock, flags);

    if (unlikely(signal_pending_state(state, current))) {

        /*

         * Exclusive waiter must not fail if it was selected by wakeup,

         * it should "consume" the condition we were waiting for.

         *

         * The caller will recheck the condition and return success if

         * we were already woken up, we can not miss the event because

         * wakeup locks/unlocks the same q->lock.

         *

         * But we need to ensure that set-condition + wakeup after that

         * can't see us, it should wake up another exclusive waiter if

         * we fail.

         */

        list_del_init(&wait->task_list);

        ret = -ERESTARTSYS;

    } else {

        if (list_empty(&wait->task_list)) {

            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 ret;

}

从prepare_to_wait_event 这个函数可知原来WQ_FLAG_EXCLUSIVE 是的作用是将wait queue降到到wait_queue_head_t的最后呀

其次调用

#define ___wait_is_interruptible(state)                    \

    (!__builtin_constant_p(state) ||                \

        state == TASK_INTERRUPTIBLE || state == TASK_KILLABLE)    \

来判断当前task的状态不能为TASK_RUNNING和TASK_INTERRUPTIBLE和 TASK_KILLABLE。如果已经是这三个state中的一个,且__int不为NULL,也就是

long __int = prepare_to_wait_event(&wq, &__wait, state) 执行成功了。就直接返回,这个时候就说明在在prepare_to_wait_event

中调用set_current_state(state);来设置task的状态failed了,这个时候就只有报错返回了.

如果前面执行顺利,就调用cmd也就是就是schedule()让出当前cpu,因为当前task的state已经不是running状态了,肯定要让出cpu.

如果wakeup后且条件满足,也即执行下面的code

        if (condition)                        \

            break;                        \

返回,最后调用finish_wait。

如果条件不满足,继续从for循环开始执行,过程和上面的类似.也就是prepare_to_wait_event 会被执行多次,所以才会调用init_wait_entry,让autoremove_wake_function没执行一次,就删除一次wait->task_list
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: