备注
AI Translation Notice
This document was automatically translated by Qwen/Qwen3-8B
model, for reference only.
Source document: kernel/sched/c_waiting.md
Translation time: 2025-05-19 01:43:09
Translation model:
Qwen/Qwen3-8B
Please report issues via Community Channel
APIs Related to “Waiting” (C Language)
警告
As the kernel evolves, we will gradually replace the C language waiting mechanism with the Rust language waiting mechanism. During this process, we will retain both the C and Rust waiting mechanisms to allow for comparison during development. Once the timing is ripe, we will gradually remove the C language waiting mechanism.
If several processes need to wait for an event to occur before they can be executed, a “waiting” mechanism is required to achieve process synchronization.
I. wait_queue Waiting Queue
wait_queue is a process synchronization mechanism, known as “waiting queue” in Chinese. It can suspend the current process and wake them up when the time is ripe, by another process.
When you need to wait for an event to complete, using the wait_queue mechanism can reduce the overhead of process synchronization. Compared to overusing spinlocks and semaphores, or repeatedly calling usleep(1000) functions for synchronization, wait_queue is an efficient solution.
警告
The implementation of the wait_queue in wait_queue.h
does not have an independent queue head and does not consider locking the wait_queue. Therefore, in later development, the queue head implementation in wait_queue_head_t
was added, which is essentially a linked list plus a spinlock. It is compatible with the queue wait_queue_node_t
in wait_queue.h
. When you use struct wait_queue_head
as the queue head, you can still use the functions to add nodes to the waiting queue.
Simple Usage
The usage of a waiting queue mainly includes the following parts:
Creating and initializing a waiting queue
Using the
wait_queue_sleep_on_
series of functions to suspend the current process. Processes that are suspended later will be placed at the end of the queue.Using the
wait_queue_wakeup()
function to wake up processes waiting in the waiting queue and add them to the scheduling queue
To use wait_queue, you need to #include<common/wait_queue.h>
, and create a wait_queue_node_t
type variable as the head of the waiting queue. This structure contains only two member variables:
typedef struct
{
struct List wait_list;
struct process_control_block *pcb;
} wait_queue_node_t;
For the waiting queue, there is a good naming method:
wait_queue_node_t wq_keyboard_interrupt_received;
This naming convention increases code readability and makes it easier to understand what the code is waiting for.
Initializing the Waiting Queue
The function wait_queue_init(wait_queue_node_t *wait_queue, struct process_control_block *pcb)
provides the functionality to initialize a wait_queue node.
When you initialize the queue head, you only need to pass the pointer to the wait_queue head node, and set the second parameter to NULL.
Inserting a Node into the Waiting Queue
You can use the following functions to suspend the current process and insert it into the specified waiting queue. These functions have similar overall functions, but differ in some details.
Function Name |
Explanation |
---|---|
wait_queue_sleep_on() |
Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE |
wait_queue_sleep_on_unlock() |
Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE. After the current process is inserted into the waiting queue, it unlocks the given spinlock |
wait_queue_sleep_on_interriptible() |
Suspends the current process and sets the suspension state to PROC_INTERRUPTIBLE |
Waking Up a Process from the Waiting Queue
You can use the void wait_queue_wakeup(wait_queue_node_t * wait_queue_head, int64_t state);
function to wake up the first process in the specified waiting queue that has a suspension state matching the specified state
.
If there are no matching processes, no process will be woken up, as if nothing happened.
II. wait_queue_head Waiting Queue Head
The data structure is defined as follows:
typedef struct
{
struct List wait_list;
spinlock_t lock; // 队列需要有一个自旋锁,虽然目前内部并没有使用,但是以后可能会用.
} wait_queue_head_t;
The usage logic of the waiting queue head is the same as the waiting queue itself, because it is also a node of the waiting queue (just with an additional lock). The functions of wait_queue_head are basically the same as those of wait_queue, except that they include the string ***_with_node_***.
Meanwhile, the wait_queue.h file provides many macros that can make your work easier.
Provided Macros
Macro |
Explanation |
---|---|
DECLARE_WAIT_ON_STACK(name, pcb) |
Declare a wait_queue node on the stack, and bind the pcb-represented process to this node |
DECLARE_WAIT_ON_STACK_SELF(name) |
Declare a wait_queue node on the stack, and bind the current process (i.e., the process itself) to this node |
DECLARE_WAIT_ALLOC(name, pcb) |
Use |
DECLARE_WAIT_ALLOC_SELF(name) |
Use |
Creating a Waiting Queue Head
You can directly call the macro
DECLARE_WAIT_QUEUE_HEAD(m_wait_queue_head); // 在栈上声明一个队列头变量
Or manually declare
struct wait_queue_head_t m_wait_queue_head = {0};
wait_queue_head_init(&m_wait_queue_head);
Inserting a Node into the Waiting Queue
Function Name |
Explanation |
---|---|
wait_queue_sleep_with_node(wait_queue_head_t *head, wait_queue_node_t *wait_node) |
Pass in a waiting queue node, and set the suspension state of the node to PROC_UNINTERRUPTIBLE |
wait_queue_sleep_with_node_unlock(wait_queue_head_t *q, wait_queue_node_t *wait, void *lock) |
Pass in a waiting queue node, suspend the process pointed to by the node’s pcb, and set the suspension state to PROC_UNINTERRUPTIBLE. After the current process is inserted into the waiting queue, unlock the given spinlock |
wait_queue_sleep_with_node_interriptible(wait_queue_head_t *q, wait_queue_node_t *wait) |
Pass in a waiting queue node, suspend the process pointed to by the node’s pcb, and set the suspension state to PROC_INTERRUPTIBLE |
Waking Up a Process from the Waiting Queue
The wait_queue_wakeup
function in wait_queue.h
directly kfree’s the wait_node node. For stack-based wait_node, you can choose wait_queue_wakeup_on_stack(wait_queue_head_t *q, int64_t state)
to wake up the queue head node in the queue.
III. completion Completion Count
Simple Usage
The usage of completion mainly includes the following parts:
Declare a completion (can be on the stack, using kmalloc, or using an array)
Use wait_for_completion to wait for the event to complete
Use complete to wake up the waiting processes
Waiting operation
void wait_fun() {
DECLARE_COMPLETION_ON_STACK(comp); // 声明一个completion
// .... do somethind here
// 大部分情况是你使用kthread_run()创建了另一个线程
// 你需要把comp变量传给这个线程, 然后当前线程就会等待他的完成
if (!try_wait_for_completion(&comp)) // 进入等待
wait_for_completion(&comp);
}
Completion operation
void kthread_fun(struct completion *comp) {
// ...... 做一些事 .......
// 这里你确定你完成了目标事件
complete(&comp);
// 或者你使用complete_all
complete_all(&comp);
}
More Usage
In the kernel/sched/completion.c folder, you can see several functions starting with test, which are test code for the completion module and cover most of the completion functions. You can refer to these functions to learn how to use them.
Initializing Completion
The function completion_init(struct completion *x)
provides the functionality to initialize a completion. When you use DECLARE_COMPLETION_ON_STACK
to create (on the stack), it will be automatically initialized.
Completion-related wait series functions
Function Name |
Explanation |
---|---|
wait_for_completion(struct completion *x) |
Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE. |
wait_for_completion_timeout(struct completion *x, long timeout) |
Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE. After waiting for timeout time (jiffies time slice), the process is automatically awakened. |
wait_for_completion_interruptible(struct completion *x) |
Suspends the current process and sets the suspension state to PROC_INTERRUPTIBLE. |
wait_for_completion_interruptible_timeout(struct completion *x, long timeout) |
Suspends the current process and sets the suspension state to PROC_INTERRUPTIBLE. After waiting for timeout time (jiffies time slice), the process is automatically awakened. |
wait_for_multicompletion(struct completion x[], int n) |
Suspends the current process and sets the suspension state to PROC_UNINTERRUPTIBLE. (Waiting for the completion of the array’s completions) |
Completion-related complete series functions
Function Name |
Explanation |
---|---|
complete(struct completion *x) |
Indicates that an event has been completed, and wakes up one process from the waiting queue |
complete_all(struct completion *x) |
Indicates that the events related to this completion are marked as permanently completed, and wakes up all processes in the waiting queue |
Other Functions for Querying Information
Function Name |
Explanation |
---|---|
completion_done(struct completion *x) |
Checks if the completion’s done variable is greater than 0. If it is, returns true; otherwise, returns false. Adding this function before waiting may accelerate the process? (Not tested experimentally yet, needs further verification) |
try_wait_for_completion(struct completion *x) |
Checks if the completion’s done variable is greater than 0. If it is, returns true (and decrements done by 1); otherwise, returns false. Adding this function before waiting may accelerate the process? (This function has the same logic as |