首页 > 技术文章 > 等待队列(转)

Przz 2017-05-29 17:29 原文

转载:http://www.cnblogs.com/zhuyp1015/archive/2012/06/09/2542894.html


突然想到epoll的内核实现,但是有点不明白设置了回调函数添加到等待队列后是如何唤醒的。所以找到了这篇文章。


Linux将进程状态描述为如下五种:

TASK_RUNNING:可运行状态。处于该状态的进程可以被调度执行而成为当前进程。

TASK_INTERRUPTIBLE:可中断的睡眠状态。处于该状态的进程在所需资源有效时被唤醒,也可以通过信号或定时中断唤醒(因为有signal_pending()函数)。

TASK_UNINTERRUPTIBLE:不可中断的睡眠状态。处于该状态的进程仅当所需资源有效时被唤醒。

TASK_ZOMBIE:僵尸状态。表示进程结束且已释放资源,但其task_struct仍未释放。


TASK_STOPPED:暂停状态。处于该状态的进程通过其他进程的信号才能被唤醒。

等待队列是如何睡眠一个进程和如何唤醒一个进程的。

使用等待队列前通常先定义一个等待队列头:static wait_queue_head_t wq ,然后调用wait_event_*函数将等待某条件condition的当前进程插入到等待队列wq中并睡眠,一直等到condition条件满足后,内核再将睡眠在等待队列wq上的某一进程或所有进程唤醒。

这里我们来分析一下唤醒的过程,举比较常用的wait_event_interruptible来分析:

/**

 * 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;                                                \

       if (!(condition))                                     \

              __wait_event_interruptible(wq, condition, __ret);     \

       __ret;                                                     \

})

这里很简单,判断一下condition条件是否满足,如果不满足则调用__wait_event_interruptible函数。

#define __wait_event_interruptible_timeout(wq, condition, ret)        \

do {                                                               \

       DEFINE_WAIT(__wait);                                          \

                                                               \

       for (;;) {                                            \

              prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);  \

              if (condition)                                         \

                     break;                                      \

              if (!signal_pending(current)) {                         \

                     ret = schedule_timeout(ret);                     \

                     if (!ret)                               \

                            break;                               \

                     continue;                                \

              }                                                \

              ret = -ERESTARTSYS;                             \

              break;                                             \

       }                                                       \

       finish_wait(&wq, &__wait);                             \

} while (0) 

__wait_event_interruptible首先定义了一个wait_queue_t类型的等待队列项__wait:

#define DEFINE_WAIT(name)  DEFINE_WAIT_FUNC(name, autoremove_wake_function) 

#define DEFINE_WAIT_FUNC(name, function)                           \

       wait_queue_t name = {                                    \

              .private = current,                         \

              .func            = function,                       \

              .task_list      = LIST_HEAD_INIT((name).task_list),    \

       }

可以发现,这里__wait的private成员(通常用来存放进程的描述符)已经被初始化为current, 表示该等待队列项对应为当前进程。func成员为该等待队列项对应的唤醒函数,该进程被唤醒后会执行它,已经被初始化为默认的autoremove_wake_function函数。

然后在一个for (;

推荐阅读