Linux内核等待队列相关函数注释

一、数据结构

1.1 等待队列头

  34struct wait_queue_head {
  35        spinlock_t              lock;
  36        struct list_head        head;
  37};

1.2 等待队列成员

  27struct wait_queue_entry {
  28        unsigned int            flags;
  //私有数据,一般指向task_struct结构体
  29        void                    *private;
  //这个函数可以唤醒等待进程
  30        wait_queue_func_t       func;
  //把自己插入链表需要这个
  31        struct list_head        entry;
  32};

二、函数

2.1 进程等待

等待队列元素插入等待队列头
设置进程的状态
调用schedule函数放弃cpu

等待事件的函数:

wait_event:这个里面进程的状态会被设置成TASK_UNINTERRUPTIBLE
wait_event_interruptible:这个函数里面进程会被设置为TASK_INTERRUPTIBLE
wait_event_interruptible_timeout:这个是带有超时的等待

2.1.1 wait_event

调用这个函数的进程将加入等待队列,函数定义为定义为:

//第一个参数就是进程要加入的等待队列的头
//第二个参数是等待的条件:例如a!=b。
 #define wait_event(wq_head, condition)                                          \
 do {                   
		 //CONFIG_DEBUG_ATOMIC_SLEEP内核选项不打开的情况下,什么也不做                                                         \
         might_sleep();
         //如果条件满足了,啥也不干就break,条件没满足就下一步__wait_event;                                                          \
         if (condition)                                                          \
                 break;                                                          \
         __wait_event(wq_head, condition);                                       \
 } while (0)
2.1.2 __wait_event:
 #define __wait_event(wq_head, condition)                                        \
 //最后一个参数是schedule()函数,调用这个函数放弃CPU
         (void)___wait_event(wq_head, condition, TASK_UNINTERRUPTIBLE, 0, 0,     \
                            schedule())

___wait_event:

259#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd)           \
 260({                                                                              \
 261        __label__ __out;                                                        \
 262        struct wait_queue_entry __wq_entry;                                     \
 263        long __ret = ret;       /* explicit shadow */                           \
 264                                                                                \
 //初始化等待队列的成员,等待队列的成员
 //函数里面有一句:wq_entry->private = current,private指向当前进程的进程描述符
 //这个函数里面指定了等待队列的唤醒函数为autoremove_wake_function
 265        init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);        \
 266        for (;;) {                                                              \
 //调用这个函数,改变进程状态
 267                long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);\
 268                                                                                \
 //被唤醒了就break
 269                if (condition)                                                  \
 270                        break;                                                  \
 271                                                                                \
 272                if (___wait_is_interruptible(state) && __int) {                 \
 273                        __ret = __int;                                          \
 274                        goto __out;                                             \
 275                }                                                               \
 276                                                                                \
 //这个就是schedule()函数,到这里进程放弃了CPU
 277                cmd;                                                            \
 278        }                                                                       \
 //将当前进程的状态设置为:TASK_RUNNING
 279        finish_wait(&wq_head, &__wq_entry);                                     \
 280__out:  __ret;                                                                  \
 281})
2.1.3 prepare_to_wait_event:
 258long prepare_to_wait_event(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry, int state)
 259{
 260        unsigned long flags;
 261        long ret = 0;
 262
 263        spin_lock_irqsave(&wq_head->lock, flags);
 264        if (signal_pending_state(state, current)) {
 265                /*
 266                 * Exclusive waiter must not fail if it was selected by wakeup,
 267                 * it should "consume" the condition we were waiting for.
 268                 *
 269                 * The caller will recheck the condition and return success if
 270                 * we were already woken up, we can not miss the event because
 271                 * wakeup locks/unlocks the same wq_head->lock.
 272                 *
 273                 * But we need to ensure that set-condition + wakeup after that
 274                 * can't see us, it should wake up another exclusive waiter if
 275                 * we fail.
 276                 */
 277                list_del_init(&wq_entry->entry);
 278                ret = -ERESTARTSYS;
 279        } else {
 280                if (list_empty(&wq_entry->entry)) {
 281                        if (wq_entry->flags & WQ_FLAG_EXCLUSIVE)
 282                                __add_wait_queue_entry_tail(wq_head, wq_entry);
 283                        else
 284                                __add_wait_queue(wq_head, wq_entry);
 285                }
 286                set_current_state(state);
 287        }
 288        spin_unlock_irqrestore(&wq_head->lock, flags);
 289
 290        return ret;
 291}
  • 286行调用了set_current_state(state),设置当前进程的状态,进程的状态在__wait_event里面传递进来的,为:TASK_UNINTERRUPTIBLE,不可中断的睡眠(也就是不能被信号唤醒,只能在等待的事件发生后唤醒)。

2.2 add_wait_queue

  18void add_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)
  19{
  20        unsigned long flags;
  21
  22        wq_entry->flags &= ~WQ_FLAG_EXCLUSIVE;
  23        spin_lock_irqsave(&wq_head->lock, flags);
  24        __add_wait_queue(wq_head, wq_entry);
  25        spin_unlock_irqrestore(&wq_head->lock, flags);
  26}

下面看__add_wait_queue的定义:

 167static inline void __add_wait_queue(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry)
 168{
 169        list_add(&wq_entry->entry, &wq_head->head);
 170}

2.2进程唤醒

2.2.1wake

函数定义:

#define wake_up(x)                      __wake_up(x, TASK_NORMAL, 1, NULL)
//第一个参数就是等待队列的头
 139void __wake_up(struct wait_queue_head *wq_head, unsigned int mode,
 140                        int nr_exclusive, void *key)
 141{
 142        __wake_up_common_lock(wq_head, mode, nr_exclusive, 0, key);
 143}
 110static void __wake_up_common_lock(struct wait_queue_head *wq_head, unsigned int mode,
 111                        int nr_exclusive, int wake_flags, void *key)
 112{
 113        unsigned long flags;
 114        wait_queue_entry_t bookmark;
 115
 116        bookmark.flags = 0;
 117        bookmark.private = NULL;
 118        bookmark.func = NULL;
 119        INIT_LIST_HEAD(&bookmark.entry);
 120
 121        do {
 122                spin_lock_irqsave(&wq_head->lock, flags);
 123                nr_exclusive = __wake_up_common(wq_head, mode, nr_exclusive,
 124                                                wake_flags, key, &bookmark);
 125                spin_unlock_irqrestore(&wq_head->lock, flags);
 126        } while (bookmark.flags & WQ_FLAG_BOOKMARK);
 127}
 128
  66static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode,
  67                        int nr_exclusive, int wake_flags, void *key,
  68                        wait_queue_entry_t *bookmark)
  69{
  70        wait_queue_entry_t *curr, *next;
  71        int cnt = 0;
  72
  73        lockdep_assert_held(&wq_head->lock);
  74
  75        if (bookmark && (bookmark->flags & WQ_FLAG_BOOKMARK)) {
  76                curr = list_next_entry(bookmark, entry);
  77
  78                list_del(&bookmark->entry);
  79                bookmark->flags = 0;
  80        } else
  81                curr = list_first_entry(&wq_head->head, wait_queue_entry_t, entry);
  82
  83        if (&curr->entry == &wq_head->head)
  84                return nr_exclusive;
  85//遍历睡眠链表,调用唤醒函数
  86        list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) {
  87                unsigned flags = curr->flags;
  88                int ret;
  89
  90                if (flags & WQ_FLAG_BOOKMARK)
  91                        continue;
  92//调用等待队列成员的fun函数
  93                ret = curr->func(curr, mode, wake_flags, key);
  94                if (ret < 0)
  95                        break;
  96                if (ret && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
  97                        break;
  98
  99                if (bookmark && (++cnt > WAITQUEUE_WALK_BREAK_CNT) &&
 100                                (&next->entry != &wq_head->head)) {
 101                        bookmark->flags = WQ_FLAG_BOOKMARK;
 102                        list_add_tail(&bookmark->entry, &next->entry);
 103                        break;
 104                }
 105        }
 106
 107        return nr_exclusive;
 108}

三、唤醒函数

2.1.2中:___wait_event函数里面调用了:
init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0);
这个函数定义如下:

 249void init_wait_entry(struct wait_queue_entry *wq_entry, int flags)
 250{
 251        wq_entry->flags = flags;
 252        wq_entry->private = current;
 253        wq_entry->func = autoremove_wake_function;//唤醒函数
 254        INIT_LIST_HEAD(&wq_entry->entry);
 255}
 370int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key)
 371{
 //内核默认的唤醒函数
 372        int ret = default_wake_function(wq_entry, mode, sync, key);
 373
 //把相应的等待队列的元素从链表中删除
 374        if (ret)
 375                list_del_init(&wq_entry->entry);
 376
 377        return ret;
 378}
//第一个参数是等待队列的元素
//第二个参数是进程状态,只有处于这个状态的进程才能被唤醒
//第三个参数是唤醒方式:0代表同步唤醒,1代表非同步唤醒
//第四个参数是唤醒时执行的函数
//由于wake_up(x)  __wake_up(x, TASK_NORMAL, 1, NULL)传递的key是NULL,所以wake_up唤醒后不执行任何函数
4336int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags,
4337                          void *key)
4338{
4339        return try_to_wake_up(curr->private, mode, wake_flags);
4340}

try_to_wake_up这个函数比较长,分析其重要的部分:
(1)设置进程的状态为:TASK_RUNNING
(2)把进程插入本地cpu的运行队列

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值