等待队列 wait queue

等待队列 wait queue.用于是进程等待某一特定事件发生,而无需频繁轮询。 进程在等待期间睡眠,在事件发生时内核自动唤醒。这点与tasklet不同,tasklet需要频繁轮询。
完成量 completion 机制基于等待队列,内核利用该机制等待某一操作结束。
 
1. 数据结构
每个等待队列 都有一个队列头:

    struct __wait_queue_head {
        spinlock_t lock;
        struct list_head task_list;  //双链表,用于实现双链表最擅长表示的结构,即队列。
    };
    typedef struct __wait_queue_head wait_queue_head_t;

因为等待队列也可以在中断时修改,所以在操作队列之前必须获得一个自旋锁 spinlock.

    typedef struct __wait_queue wait_queue_t;

 

    struct __wait_queue {
        unsigned int flags;  //要么等于WQ_FLAG_EXCLUSIVE,表示等待进程想要被独占的唤醒,要么等于0.
    #define WQ_FLAG_EXCLUSIVE    0x01
        void *private;   //指向等待进程的task_struct实例
        wait_queue_func_t func; //唤醒等待进程
        struct list_head task_list;  //链表元素,将wait_queue_t实例放置到等待队列中。
    };

2. 使进程睡眠 add_wait_queue()

    void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
    {
        unsigned long flags;
        wait->flags &= ~WQ_FLAG_EXCLUSIVE;
        spin_lock_irqsave(&q->lock, flags);
        __add_wait_queue(q, wait);
        spin_unlock_irqrestore(&q->lock, flags);
    }
    EXPORT_SYMBOL(add_wait_queue);

add_wait_queue()通常不直接使用,更常用的是wait_event().

    static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
    {
        list_add(&new->task_list, &head->task_list); //将新进程 添加到等待队列中。
    }

使进程在等待队列上睡眠的另一种方法:

    /*
     * Note: we use "set_current_state()" _after_ the wait-queue add,
     * because we need a memory barrier there on SMP, so that any
     * wake-function that tests for the wait-queue being active
     * will be guaranteed to see waitqueue addition _or_ subsequent
     * tests in this thread will see the wakeup having taken place.
     *
     * The spin_unlock() itself is semi-permeable and only protects
     * one way (it only protects stuff inside the critical region and
     * stops them from bleeding out - it would still allow subsequent
     * loads to move into the critical region).
     */
    void fastcall
    prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
    {
        unsigned long flags;
        wait->flags &= ~WQ_FLAG_EXCLUSIVE;
        spin_lock_irqsave(&q->lock, flags);
        if (list_empty(&wait->task_list))
            __add_wait_queue(q, wait);
        /*
         * don't alter the task state if this is just going to
         * queue an async wait queue callback
         */
        if (is_sync_wait(wait))
            set_current_state(state);
        spin_unlock_irqrestore(&q->lock, flags);
    }
    EXPORT_SYMBOL(prepare_to_wait);

3. 初始化一个等待队列项:

a. init_waitqueue_entry():

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

b.DEFINE_WAIT 创建一个wait_queue_t的静态实例:

    #define DEFINE_WAIT(name)                        \
        wait_queue_t name = {                        \
            .private    = current,                \
            .func        = autoremove_wake_function,        \
            .task_list    = LIST_HEAD_INIT((name).task_list),    \
        }

4. wait_event

add_wait_queue()通常不直接使用,更常用的是wait_event().

    /**
     * wait_event - 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_UNINTERRUPTIBLE) until the
     * @condition evaluates to true. 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.
     */
    #define wait_event(wq, condition)                     \
    do {                                    \
        if (condition)                             \
            break;                            \
        __wait_event(wq, condition);                    \
    } while (0)

5. 唤醒进程 wake_up()

    #define wake_up(x)            __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)
    #define wake_up_nr(x, nr)        __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr, NULL)
    #define wake_up_all(x)            __wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0, NULL)
    #define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
    #define wake_up_interruptible_nr(x, nr)    __wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
    #define wake_up_interruptible_all(x)    __wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
    #define    wake_up_locked(x)        __wake_up_locked((x), TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)
    #define wake_up_interruptible_sync(x) __wake_up_sync((x),TASK_INTERRUPTIBLE, 1)

    /**
     * __wake_up - wake up threads blocked on a waitqueue.
     * @q: the waitqueue
     * @mode: which threads
     * @nr_exclusive: how many wake-one or wake-many threads to wake up
     * @key: is directly passed to the wakeup function
     */
    void fastcall __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);
    }
    EXPORT_SYMBOL(__wake_up);

    /*
     * The core wakeup function. Non-exclusive wakeups (nr_exclusive == 0) just
     * wake everything up. If it's an exclusive wakeup (nr_exclusive == small +ve
     * number) then we wake all the non-exclusive tasks and one exclusive task.
     *
     * There are circumstances in which we can try to wake a task which has already
     * started to run but is not in state TASK_RUNNING. try_to_wake_up() returns
     * zero in this (rare) case, and we handle it by continuing to scan the queue.
     */
    static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
                 int nr_exclusive, int sync, 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, sync, key) &&
                    (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
                break;
        }
    }

 


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值