When a process communicates in a blocking manner, the process suspends sleep before the result is obtained. In order to ensure that the process enters sleep in a safe way, we need to keep in mind two rules: 1. never enter sleep in the atomic context. 2. do not know the environment after the process is sleep. After waking up, you must check again... information & nbs
When a process communicates in a blocking manner, the process suspends sleep before the result is obtained.
To secure processes from entering sleep, we need to keep in mind two rules:
1. never sleep in the atomic context.
2. do not know the environment after the process is sleep. After waking up, you must check again to make sure that the conditions we are waiting for are true.
Simple sleep
The code for completing the wake-up task must also be able to locate our processes so that the sleeping process can be awakened. A data structure called waiting queue needs to be maintained. A waiting queue is a process linked list that contains all processes waiting for a specific event.
Linux maintains a "waiting queue header" for management. wait_queue_head_t is defined in
Struct _ wait_queue_head {
Wq_lock_t lock;
Struct list_head task_list;
};
Typedef struct _ wait_queue_head wait_queue_head_t;
Initialization method:
Static method:
DECLARE_WAIT_QUEUE_HEAD (name)
Dynamic method:
Wait_queue_head_t my_queue;
Init_waitqueue_head (& my_queue );
The simplest way to sleep in linux is the following macro,
Wait_event (queue, condition)/* The process will be placed in non-interrupted sleep (uninterruptible sleep )*/
Wait_event_interruptible (queue, condition)/* The process can be interrupted by signals to sleep. if a non-zero value is returned, it indicates that sleep is interrupted by signals */
Wait_event_timeout (queue, condition, timeout)/* wait for a limited time jiffy. if one of the conditions is met, 0 is returned */
Wait_event_interruptible_timeout (queue, condition, timeout)
Queue is the waiting queue header. it is a value-passing method.
Condition is a Boolean expression that evaluates condition multiple times before and after hibernation. if it is true, it will wake up.
The basic function to wake up a process is wake_up.
Void wake_up (wait_queue_head_t * queue);/* wake up all processes waiting for a given queue */
Void wake_up_interruptible (wait_queue_head_t * queue );
In practice, wait_event and wake_up, wait_event_interruptible are used in pairs with wake_up_interruptible.
Advanced sleep
To sleep a process:
(1) allocate and initialize a wait_queue_t structure, and then add it to the correct waiting queue
Struct _ wait_queue {
Unsigned int flags;
Void * private;
Wait_queue_func_t func;
Struct list_head task_list;
};
Typedef struct _ wait_queue wait_queue_t;
(2) set the process status and mark it as sleep. In the 2.6 kernel, use the following functions:
Void set_current_state (int new_state );
In Task _ running indicates that the process can run. Two statuses indicate that a process is sleeping: TASK_INTERRUPTIBLE and TASK_UNTINTERRUPTIBLE.
(3) the last step is to discard the processor. However, you must first check the conditions for entering sleep. If no check is performed, the race State will be introduced: if other threads are trying to wake you up when you are busy with the above process, you may miss the wake-up and sleep for a long time. Therefore, under typical code
If (! Condition)
Schedule ();/* call the scheduler and let out the CPU */
If the code is only returned from schedule, the process is in the TASK_RUNNING state. If you skip the call to schedule without having to sleep, you must reset the task status to TASK_RUNNING, and remove the process from the waiting queue; otherwise, it may be awakened multiple times.
Manual sleep
You can manually set the process sleep steps above:
(1) create and initialize a waiting queue. It is often defined by a macro:
DEFINE_WAIT (my_wait );
Name is the name of the waiting queue entry. you can also perform the following two steps:
Wait_queue_t my_wait;
Init_wait (& my_wait );
A common practice is to put DEFINE_WAIT on the top of the loop to sleep.
(2) add the waiting queue entry to the queue and set the process Status:
Void prepare_to_wait (wait_queue_head_t * queue,
Wait_queue_t * wait,
Int state );
Queue and wait are respectively waiting queue headers and process portals. State is the new state of the process: TASK_INTERRUPTIBLE (can interrupt sleep, recommended) or TASK_UNINTERRUPTIBLE (cannot interrupt sleep, not recommended)
(3) Call schedule after checking whether sleep is required
If (! Condition)
Schedule ();/* call the scheduler and let out the CPU */
(4) When schedule returns, the cleanup time is reached:
Void finish_wait (wait_queue_head_t * queue, wait_queue_t * wait );
The underlying source code of wait_event (queue, condition) and wait_event_interruptible (queue, condition) in simple sleep will find that they are only a combination of functions in manual sleep. It is better to use wait_event if you are in trouble.
Exclusive waiting
When a process calls wake_up on the waiting queue, all the processes waiting on the queue are set to run. This is a correct practice in many cases. However, sometimes only one wake-up process will successfully obtain the required resources, and the rest will sleep again. In this case, if the number of processes in the waiting queue is large, this may seriously reduce system performance. Therefore, the kernel developer adds an exclusive wait option. It has two important differences with a normal sleep:
(1) when the WQ_FLAG_EXCLUSEVE flag is set for the waiting queue entry, it is added to the end of the waiting queue; otherwise, it is added to the header.
(2) when wake_up is called in a waiting queue, it stops the wake-up after the first process with the WQ_FLAG_EXCLUSIVE flag. However, the kernel still wakes up all non-exclusive waits each time.
Two conditions must be met for an exclusive wait:
(1) hope to effectively compete for resources;
(2) when resources are available, it is enough to wake up a process to completely consume resources.
Enable a process to enter the exclusive waiting state, which can be called:
Void prepare_to_wait_exclusive (wait_queue_head_t * queue, wait_queue_t * wait, int state );
Note: wait_event and its variants cannot be used for exclusive waiting.
Wake-up function
It is seldom necessary to call wake-up functions other than wake_up_interruptible, but for the sake of completeness, here is the entire set:
Wake_up (wait_queue_head_t * queue );
Wake_up_interruptible (wait_queue_head_t * queue );
Wake_up wakes up each non-exclusive waiting process and one exclusive waiting process in the queue. Wake_up_interruptible, in addition to skipping a process in which the sleep cannot be interrupted. Before they are returned, one or more processes are awakened and scheduled (this will not happen if they are called from an atomic context ).
Wake_up_nr (wait_queue_head_t * queue, int nr );
Wake_up_interruptible_nr (wait_queue_head_t * queue, int nr );
These functions are similar to wake_up, except that they can wake up to nr exclusive waits, not just one. Note that passing 0 is interpreted as a request for all mutex waits to be awakened
Wake_up_all (wait_queue_head_t * queue );
Wake_up_interruptible_all (wait_queue_head_t * queue );
This kind of wake_up will wake up all processes, regardless of whether they have exclusive waits (the type of interruptions still skips the processes that are waiting for non-disruptive waits)
Wake_up_interruptible_sync (wait_queue_head_t * queue );
A wake-up process may seize the current process and be scheduled to the processor before wake_up returns. However, you can use the "synchronization" variant of wake_up_interruptible if you do not need to be scheduled out of the processor. this function is most commonly used when the caller first needs to complete a small amount of work and does not want to be scheduled out of the processor.
Author FBI888XH