linux wait_event and wake_up

来源:互联网 发布:r软件介绍 编辑:程序博客网 时间:2024/05/16 15:35

windows编程中有CreatEevent SetEvent WaitForSingleObject相关的event机制,来实现非阻塞的等待及事件的通知。

linux有同样类似的实现:

wait_event(wait_queue_head_t   wq, condition)   和 wake_up(wait_queue_head_t * x).

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

在来看

#define __wait_event(wq, condition)                     \
do {                                    \
    DEFINE_WAIT(__wait);                        \
                                    \
    for (;;) {                            \
        prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE);    \
        if (condition)                        \
            break;                        \
        schedule();                        \
    }                                \
    finish_wait(&wq, &__wait);                    \
} while (0)
这里DEFINE_WAIT创建一个当前进程的wait_queue_t结构, prepare_to_wait将它加入之前我们传入的wait_queue_head_t   wq(这个要在使用前调用    init_waitqueue_head(&wq)初始化过)同时将当前进程状态设置成TASK_UNINTERRUPTIBLE,代码实现如下:

/*
 * 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
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);
    set_current_state(state);
    spin_unlock_irqrestore(&q->lock, flags);
}

由于之前设置了TASK_UNINTERRUPTIBLE, 这样当调用到schedule()的时候,当前进程会被移除调度队列。直到内核来唤醒它。

当我们认为condition有可能变化的时候,需要调用wake_up来唤醒进程,进程会在__wait_event循环中判断条件是否满足。如果满足则跳出循环继续执行,如果不满足(不是真,这里条件可以是一个表达式或者函数或者变量),则继续进入睡眠。

下面看下wake_up的实现:

#define wake_up(x)            __wake_up(x, TASK_NORMAL, 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);
}

可以看出wake_up 设置的nr_exclusive是1 ,这样只会唤醒一个进程的。同时TASK_NORMAL

#define TASK_NORMAL        (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)

它代表会唤醒处于这两个状态的进程。wake_up有很多相关的函数,这其中wait_event也是类似的对应的存在相关函数。

#define wake_up(x)            __wake_up(x, TASK_NORMAL, 1, NULL)  //这个唤醒一个进程,包括INTERRUPTIBLEUNINTERRUPTIBLE
#define wake_up_nr(x, nr)        __wake_up(x, TASK_NORMAL, nr, NULL)//唤醒 nr个进程,包括INTERRUPTIBLEUNINTERRUPTIBLE
#define wake_up_all(x)            __wake_up(x, TASK_NORMAL, 0, NULL)//唤醒队列(wq)中所有进程, 包括INTERRUPTIBLEUNINTERRUPTIBLE
#define wake_up_locked(x)        __wake_up_locked((x), TASK_NORMAL) //同wake_up 只不过是在已经去的wq的spinlock的情况下调用
//下面的是只针对TASK_INTERRUPTIBLE状态的进程才唤醒
#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_interruptible_sync(x)    __wake_up_sync((x), TASK_INTERRUPTIBLE, 1)



原创粉丝点击