linux 等待队列


wait_queue_head_t的定义(同样在wait.h中):

struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

由于我们只需要对队列进行添加和删除操作,并不会修改其中的对象(等待队列项),因此,我们只需要提供一把保护整个基础设施和所有对象的锁(lock,spinlock类型),这把锁保存在等待队列头中

在对task_list与操作的过程中,使用该锁实现对等待队列的互斥访问。
其中task_list是一个正在睡眠的进程的链表,双向循环链表,存放等待的进程 task_list中的数据成员类型为 wait_queue_t。


定义wait_queue_heat_t变量

include/linux.wait.h中对DECLARE_WAIT_QUEUE_HEAD的定义:

#define DECLARE_WAIT_QUEUE_HEAD(name) \

    wait_queue_head_t name = __WAIT_QUEUE_HEAD_INITIALIZER(name)

/*  #define __WAIT_QUEUE_HEAD_INITIALIZER(name) {                \
    .lock        = __SPIN_LOCK_UNLOCKED(name.lock),        \
    .task_list    = { &(name).task_list, &(name).task_list } }

*/


wait_queue_head_t 类型的定义:

typedef struct __wait_queue wait_queue_t;

struct __wait_queue {
    unsigned int flags;
#define WQ_FLAG_EXCLUSIVE    0x01
    void *private;
    wait_queue_func_t func;
    struct list_head task_list;
};

其中flags域指明该等待的进程是互斥进程还是非互斥进程。其中0是非互斥进程,WQ_FLAG_EXCLUSIVE(0x01)是互斥进程。等待队列(wait_queue_t)和等待对列头(wait_queue_head_t)的区别是等待队列是等待队列头的成员。也就是说等待队列头的task_list域链接的成员就是等待队列类型的(wait_queue_t)


(从等待队列头中)添加/移出等待队列:

add_wait_queue()函数:

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



sleep_on的定义:

void __sched sleep_on(wait_queue_head_t *q)
{
    sleep_on_common(q, TASK_UNINTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
}

/*

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

    init_waitqueue_entry(&wait, current);              //用当前进程生成一个wait_queue_t
    __set_current_state(state);
    spin_lock_irqsave(&q->lock, flags);
    __add_wait_queue(q, &wait);                          //把wait放到q所属的wait_queue_t list 的开头
    spin_unlock(&q->lock);
    timeout = schedule_timeout(timeout);            //放弃CPU
    spin_lock_irq(&q->lock);
    __remove_wait_queue(q, &wait);                   //将wait从等待队列头q remove下来
    spin_unlock_irqrestore(&q->lock, flags);

    return timeout;

*/

可以看出sleep_on的作用就是在sleep_on内部定义了一个与current关联的等待队列wait,将wait加入q等待队列头,更改current的state为TASK_UNINTERRUPTIBLE,

之后调度放弃cpu,等到再调度回来继续运行时,将wait从q中删除。




相应的wake_up的定义:

#define wake_up(x)                            __wake_up(x, TASK_NORMAL, 1, NULL)

#define wake_up_interruptible(x)    __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)

/**
 * __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
 *
 * It may be assumed that this function implies a write memory barrier before
 * changing the task state if and only if any tasks are woken up.
 */

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

/*
 * 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
 * zer
o 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 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;
    }
}


你可能感兴趣的:(linux 等待队列)