kernel版本:4.14.111
其中,自旋锁lock是用来防止并发访问,task_list字段是等待队列中等待的进程链表的头。
/* * A single wait-queue entry structure: */ struct wait_queue_entry { unsigned int flags; void *private; (1) wait_queue_func_t func; (2) struct list_head entry; (3) };1)通常指向当前任务task_struct数据结构 2)回调函数 3)挂入等待队列的链表
1)动态初始化
wait_queue_head_t my_queue; init_waitqueue_head(&my_queue); //会将自旋锁初始化为未锁,等待队列初始化为空的双向循环链表。2)静态定义
DECLARE_WAIT_QUEUE_HEAD (my_queue);1)首先判断条件是否满足,满足则直接返回 2)条件不满足则执行__wait_event
/* wait_event */ #define __wait_event(wq_head, condition) \ (void)___wait_event(wq_head, condition, TASK_UNINTERRUPTIBLE, 0, 0, \ schedule()) /* wait_event_interruptible */ #define __wait_event_interruptible(wq_head, condition) \ ___wait_event(wq_head, condition, TASK_INTERRUPTIBLE, 0, 0, \ schedule()) /* wait_event_timeout */ #define __wait_event_timeout(wq_head, condition, timeout) \ ___wait_event(wq_head, ___wait_cond_timeout(condition), \ TASK_UNINTERRUPTIBLE, 0, timeout, \ __ret = schedule_timeout(__ret))上述分别为wait_event 、wait_event_interruptible 、wait_event_timeout 宏。可以看出,最终都是调用了___wait_event接口。 区别仅仅在于传递的进程状态,以及执行的调度函数。
#define ___wait_event(wq_head, condition, state, exclusive, ret, cmd) \ ({ \ __label__ __out; \ struct wait_queue_entry __wq_entry; \ (1) long __ret = ret; /* explicit shadow */ \ \ init_wait_entry(&__wq_entry, exclusive ? WQ_FLAG_EXCLUSIVE : 0); \ (2) for (;;) { \ long __int = prepare_to_wait_event(&wq_head, &__wq_entry, state);\ (3) \ if (condition) \ (4) break; \ \ if (___wait_is_interruptible(state) && __int) { \ __ret = __int; \ goto __out; \ } \ \ cmd; \ (5) } \ finish_wait(&wq_head, &__wq_entry); \ (6) __out: __ret; \ })1)定义一个等待队列 2)初始化该等待队列,代码实现如下:
void init_wait_entry(struct wait_queue_entry *wq_entry, int flags) { wq_entry->flags = flags; wq_entry->private = current; //private指针设置为当前进程task_struct wq_entry->func = autoremove_wake_function; //设置默认回调函数 INIT_LIST_HEAD(&wq_entry->entry); //初始化等待队列链表 }3)for循环内调用prepare_to_wait_event
long prepare_to_wait_event(struct wait_queue_head *wq_head, struct wait_queue_entry *wq_entry, int state) { unsigned long flags; long ret = 0; spin_lock_irqsave(&wq_head->lock, flags); if (unlikely(signal_pending_state(state, current))) { list_del_init(&wq_entry->entry); ret = -ERESTARTSYS; } else { if (list_empty(&wq_entry->entry)) { if (wq_entry->flags & WQ_FLAG_EXCLUSIVE) __add_wait_queue_entry_tail(wq_head, wq_entry); else __add_wait_queue(wq_head, wq_entry); } set_current_state(state); } spin_unlock_irqrestore(&wq_head->lock, flags); return ret; }if (list_empty(&wq_entry->entry))判断当前等待队列链表是否初始化,如果未初始化,则将当前等待队列链表加入等待队列头链表中。 set_current_state,设置当前进程状态,wait_event为TASK_UNINTERRUPTIBLE wait_event_interruptible为TASK_INTERRUPTIBLE 4)调用完prepare_to_wait_event后,再次判断条件是否满足,如果条件满足,则跳出for循环。 5)如果条件不满足,则执行cmd接口,对于wait_event,为schedule,对于wait_event_timeout,为schedule_timeout。即主动执行调度。 6)跳出循环,设置当前进程状态为TSAK_RUNNING。将等待队列从等待队列头链表中删除。对于wait_event,这里是进程被wake_up,且条件满足,才会执行到这里。对于wait_event_timeout,可能是条件满足,也可能是timeout超时,根据返回值判断。
1)遍历当前等待队列头链表中的等待队列。根据传递的唤醒数量,执行循环。 2)执行等待队列定义时,传递的func回调。上文中提到了,默认回调函数为autoremove_wake_function
int autoremove_wake_function(struct wait_queue_entry *wq_entry, unsigned mode, int sync, void *key) { int ret = default_wake_function(wq_entry, mode, sync, key); if (ret) list_del_init(&wq_entry->entry); return ret; } int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags, void *key) { return try_to_wake_up(curr->private, mode, wake_flags); }autoremove_wake_function最终会调用try_to_wake_up函数将进程置为TASK_RUNNING状态。这样后面的进程调度便会调度到该进程,从而唤醒该进程继续执行。