Common usage in the kernel:
The kernel has a function wake_up and wake_up_interruptible. Usually, seeing these two function calls is to wake up the thread on the waiting queue.
Until I looked at the source code of epoll, I found that this was not the case.
bool wakeup_condition; wait_queue_head_t wait_queue; init_waitqueue_head(&wait_queue); wait_queue_entry_t wq_entry // wait wait_event_interruptible(&wait_queue, wakeup_condition || kthread_should_stop()); // Wake up// Set the wait condition to true and wake up wakeup_condition = true; wake_up(&wait_queue);
Wake_up source code:
// common/include/linux/ #define TASK_NORMAL (TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE) #define wake_up(x) __wake_up(x, TASK_NORMAL, 1, NULL) #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL) // common/kernel/sched/ // wake_up is a macro, and after expansion, the __wake_up function is called// __wake_up(x, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, NULL) int __wake_up(struct wait_queue_head *wq_head, unsigned int mode, int nr_exclusive, void *key) { return __wake_up_common_lock(wq_head, mode, nr_exclusive, 0, key); } EXPORT_SYMBOL(__wake_up); // __wake_up_common_lock(wq_head, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, 0, NULL) static int __wake_up_common_lock(struct wait_queue_head *wq_head, unsigned int mode, int nr_exclusive, int wake_flags, void *key) { unsigned long flags; wait_queue_entry_t bookmark; int remaining = nr_exclusive; = 0; = NULL; = NULL; INIT_LIST_HEAD(&);//Initialize the linked list: The next and prev pointers of the linked list point to the linked list's own address do { spin_lock_irqsave(&wq_head->lock, flags);//Spin lock lock, lock the queue remaining = __wake_up_common(wq_head, mode, remaining, wake_flags, key, &bookmark); spin_unlock_irqrestore(&wq_head->lock, flags);//Spin lock unlock } while ( & WQ_FLAG_BOOKMARK); return nr_exclusive - remaining;//When the queue is empty, remaining=nr_exclusive, at this time return 0;} // __wake_up_common(wq_head, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, 0, NULL, &bookmark); static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode, int nr_exclusive, int wake_flags, void *key, wait_queue_entry_t *bookmark) { wait_queue_entry_t *curr, *next; int cnt = 0; lockdep_assert_held(&wq_head->lock); // = 0; WQ_FLAG_BOOKMARK = 0x04; if (bookmark && (bookmark->flags & WQ_FLAG_BOOKMARK)) {//This branch will not be entered curr = list_next_entry(bookmark, entry); list_del(&bookmark->entry); bookmark->flags = 0; } else curr = list_first_entry(&wq_head->head, wait_queue_entry_t, entry);//Get the first element of the wq_head queue if (&curr->entry == &wq_head->head)// When the queue is empty, the passed nr_exclusive will be directly returned to the passed nr_exclusive return nr_exclusive; list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) {//Transfer the link table unsigned flags = curr->flags; int ret; if (flags & WQ_FLAG_BOOKMARK) continue; /* Calling the callback function func in wait_queue_entry_t // Different results will appear here depending on the type of func. Use init_waitqueue_entry initialized wait_queue_entry, func = default_wake_function, this function will wake up the thread on curr->private. The wait_queue_entry_t initialized with init_waitqueue_func_entry is just to make ordinary function calls. */ ret = curr->func(curr, mode, wake_flags, key); if (ret < 0) break; if (ret && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) break; if (bookmark && (++cnt > WAITQUEUE_WALK_BREAK_CNT) && (&next->entry != &wq_head->head)) { bookmark->flags = WQ_FLAG_BOOKMARK; list_add_tail(&bookmark->entry, &next->entry); break; } } return nr_exclusive; }
func assignment process
wait_queue_head and wait_queue_entry data structures
//Kernel 4.14 after// common/include/linux/ struct wait_queue_head {// wait queue spinlock_t lock; // Spin lock struct list_head head; // When adding to the wait queue, wait_queue_entry.entry is added to this head link table}; /* * A single wait-queue entry structure: */ struct wait_queue_entry {// An item in the wait queue unsigned int flags; void *private; // Private data, representing threads in init_waitqueue_entry, and null in init_waitqueue_func_entry wait_queue_func_t func; // Callback function struct list_head entry; // When adding to the wait queue, this entry is added to the link table of wait_queue_head.head}; typedef struct wait_queue_head wait_queue_head_t; // wait_queue_head_t same as wait_queue_headtypedef struct wait_queue_entry wait_queue_entry_t; // wait_queue_entry_t same as wait_queue_entry
forwait_queue_entry
There are two commonly used initialization methodsinit_waitqueue_entry
andinit_waitqueue_func_entry
Two waiting tasks: wait_queue_entry: thread and function
// common/include/linux/ static inline void init_waitqueue_entry(struct wait_queue_entry *wq_entry, struct task_struct *p) { wq_entry-&gt;flags = 0; wq_entry-&gt;private = p; //Storage threads that need to be woken into private data // func assigns the default_wake_function function // The function of this function is to wake up the thread on the waiting queue wq_entry-&gt;func = default_wake_function; // This function is: wake up the thread p} static inline void init_waitqueue_func_entry(struct wait_queue_entry *wq_entry, wait_queue_func_t func) { wq_entry-&gt;flags = 0; wq_entry-&gt;private = NULL; wq_entry-&gt;func = func; // Assign the incoming callback function directly to wq_entry->func}
default_wake_function function
The function of this function is basically equivalent towake_up_process
function.
int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags, void *key) { WARN_ON_ONCE(IS_ENABLED(CONFIG_SCHED_DEBUG) &amp;&amp; wake_flags &amp; ~WF_SYNC); //The try_to_wake_up function achieves the purpose of waking up a sleepy and stopping process by setting the process state to TASK_RUNNING and inserting the process into the local CPU running queue rq. // curr->private stores threads that need to be woken up return try_to_wake_up(curr-&gt;private, mode, wake_flags); } EXPORT_SYMBOL(default_wake_function);
To sum up:
- wake_up may be to wake up the thread on the queue, or it may just trigger a callback.
Two uses of wake_up:
bool wakeup_condition; wait_queue_head_t wait_queue; init_waitqueue_head(&wait_queue); wait_queue_entry_t wq_entry // wait The first usage:Thread waiting wait_event_interruptible(&wait_queue, wakeup_condition || kthread_should_stop()); The second usage:Add a callback to the waiting queue init_waitqueue_func_entry(&wq_entry, callback); add_wait_queue(&wait_queue, &wq_entry); // Wake up Set the waiting condition totrue,And wake up wakeup_condition = true; // traverse the queue internally and call each wait_queue_entry func function, which produces different effects according to the func wake_up(&wait_queue);
Note: Based on the analysis of Kernel 4.14 later versions, please pay attention to my other related articles about the Android kernel code wake_up!