January 09, 2025

Linux process sleep and wake up

1 Sleep and wake up of Linux processes

In Linux, processes that wait only for CPU time are called ready processes, they are placed in a run queue, and the status flag for a ready process is TASK_RUNNING. Once a running process time slice is exhausted, the Linux kernel's scheduler deprives the process of control of the CPU and selects a suitable process from the run queue to run.

Of course, a process can also actively release control of the CPU. The function schedule() is a dispatch function that can be actively called by a process to schedule other processes to occupy the CPU. Once the process that voluntarily relinquishes the CPU is rescheduled to occupy the CPU, it will execute from the location where it was last stopped, that is, it will execute from the next line of code that calls schedule().

Sometimes, the process needs to wait until a specific event occurs, such as device initialization, I/O operation completion, or timer arrival. In this case, the process must be removed from the run queue and added to a wait queue, at which point the process goes to sleep.

There are two types of process sleep states in Linux: one is the interruptible sleep state, and its status flag

TASK_INTERRUPTIBLE;

The other is an uninterruptible sleep state with a status flag of TASK_UNINTERRUPTIBLE. An interruptible sleep state process sleeps until a condition becomes true. For example, generating a hardware interrupt, releasing system resources that the process is waiting for, or passing a signal can be a condition for waking up the process. The uninterruptible sleep state is similar to the interruptible sleep state, but with one exception, the process of passing a signal to this sleep state cannot change its state, that is, it does not respond to wake-up of the signal. Non-interruptible sleep states are generally less common, but they are useful in some specific situations. For example, a process must wait and cannot be interrupted until a particular event occurs.

In modern Linux operating systems, processes typically go to sleep with a call to schedule(). The following code demonstrates how to put a running process to sleep.

Sleeping_task = current;

Set_current_state(TASK_INTERRUPTIBLE);

Schedule();

Func1();

/* Rest of the code ... */

In the first statement, the program stores a process structure pointer, sleeping_task, which is a macro that points to the process structure being executed. Set_current_state() changes the state of the process from the execution state TASK_RUNNING to the sleep state TASK_INTERRUPTIBLE. If schedule() is dispatched by a process whose state is TASK_RUNNING, then schedule() will schedule another process to consume the CPU; if schedule() is scheduled by a process whose state is TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE, then there is an additional step Executed: The currently executing process will be removed from the run queue before another process is scheduled, which will cause the running process to go to sleep because it is no longer in the run queue.

We can use the following function to wake up the process that just went to sleep.

Wake_up_process(sleeping_task);

After calling wake_up_process(), the state of the sleep process is set to TASK_RUNNING and the scheduler will add it to the run queue. Of course, this process can only be put into operation the next time it is dispatched by the scheduler.

2 invalid wake up

In almost all cases, the process checks for certain conditions and finds that the condition is not met before going to sleep. However, sometimes the process will start to sleep after the judgment condition is true. If this happens, the process will sleep indefinitely. This is the so-called invalid wake-up problem. In the operating system, when multiple processes attempt to process some shared data, and the final result depends on the order in which the processes run, a race condition occurs. This is a typical problem in the operating system, invalid wakeup. It is precisely because of the competitive conditions.

Imagine two processes A and B. The A process is processing a linked list. It needs to check if the linked list is empty. If it is not empty, it will perform some operations on the data in the linked list, and the B process also adds nodes to the linked list. When the linked list is empty, since there is no data to operate, then the A process goes to sleep. When the B process adds a node to the linked list, it wakes up the A process. The code is as follows:

A process:

1 spin_lock(&list_lock);

2if(list_empty(&list_head)) {

3 spin_unlock(&list_lock);

4 set_current_state(TASK_INTERRUPTIBLE);

5 schedule();

6 spin_lock(&list_lock);

7 }

8

9/* Rest of the code ... */

10 spin_unlock(&list_lock);

B process:

100 spin_lock(&list_lock);

101 list_add_tail(&list_head, new_node);

102 spin_unlock(&list_lock);

103 wake_up_process(processa_task);

There is a problem here. If the A process is executed before the 4th line after the 3rd line, the B process is scheduled to be put into operation by another processor. During this time slice, the B process has executed all its instructions, so it tries to wake up the A process, and the A process has not yet gone to sleep, so the wakeup operation is invalid. After this, the A process continues to execute, it will mistakenly believe that the list is still empty at this time, so set its own state to TASK_INTERRUPTIBLE and then call schedule() to go to sleep. Since the B process wakes up, it will sleep indefinitely. This is an invalid wakeup problem, because even if there is data in the linked list to be processed, the A process still sleeps.

3 Avoid invalid wakeup

How to avoid invalid wake-up problems? We found that the invalid wakeup occurred mainly after the check condition and before the process state was set to sleep state. The wakeup_process() of the original B process provided an opportunity to set the state of the A process to TASK_RUNNING. Unfortunately, the state of the A process is still TASK_RUNNING. Therefore, the effort of wake_up_process() to change the state of the A process from sleep to run did not work as expected. To solve this problem, it is necessary to use a guarantee mechanism to make the judgment list empty and set the process state to sleep state as an inseparable step, that is, the root cause of the race condition must be eliminated, so that wake_up_process appears after this ( ) It is possible to play the role of a process in which the awake state is a sleep state. After finding the reason, redesigning the code structure of the A process can avoid the invalid wakeup problem in the above example.

A process:

1 set_current_state(TASK_INTERRUPTIBLE);

2 spin_lock(&list_lock);

3if(list_empty(&list_head)) {

4 spin_unlock(&list_lock);

5 schedule();

6 spin_lock(&list_lock);

7 }

8 set_current_state(TASK_RUNNING);

9

10/* Rest of the code ... */

11 spin_unlock(&list_lock);

As you can see, this code sets the current execution process state to TASK_INTERRUPTIBLE before the test condition, and sets itself to the TASK_RUNNING state if the linked list is not empty. This way, if the B process calls wake_up_process() after the A process process checks that the linked list is empty, then the state of the A process will automatically change from the original TASK_INTERRUPTIBLE to TASK_RUNNING, after which the process calls schedule() again, since it is now The state is TASK_RUNNING, so it will still not be removed from the run queue, so it will not go to sleep by mistake, and of course avoid invalid wake-up.

4 examples of the Linux kernel

In the Linux operating system, the stability of the kernel is very important. In order to avoid invalid wake-up problems in the Linux operating system kernel, the Linux kernel should use an operation similar to the following when the process sleeps:

/* 'q' is the waiting queue we want to sleep*/

DECLARE_WAITQUEUE(wait,current);

Add_wait_queue(q, &wait);

Set_current_state(TASK_INTERRUPTIBLE);

/* or TASK_INTERRUPTIBLE */

While(!condition) /* 'condition' is the condition to wait*/

Schedule();

Set_current_state(TASK_RUNNING);

Remove_wait_queue(q, &wait);

The above operation makes the process safely add itself to a waiting queue for sleep through the following series of steps: first call DECLARE_WAITQUEUE () to create a waiting queue item, then call add_wait_queue() to add itself to the waiting queue. And set the state of the process to TASK_INTERRUPTIBLE or TASK_INTERRUPTIBLE. Then loop to see if the condition is true: if it is, there is no need to sleep, if the condition is not true, then schedule() is called. When the condition checked by the process is met, the process sets itself to TASK_RUNNING and calls remove_wait_queue() to move itself out of the wait queue.

As you can see from the above, the kernel code maintainer of Linux also sets the state of the process to sleep before the process check condition, and then loops the check condition. If the condition is reached before the process begins to sleep, the loop will exit and set its state to ready with set_current_state(), which also ensures that the process does not have the wrong tendency to go to sleep, and of course does not cause it to appear. Invalid wakeup issue.

Let's use the examples in the linux kernel to see how the Linux kernel avoids ineffective sleep. This code comes from the Linux2.6 kernel (linux-2.6.11/kernel/sched.c: 4254):

4253/* Wait for kthread_stop */

4254 set_current_state(TASK_INTERRUPTIBLE);

4255while (!kthread_should_stop()) {

4256 schedule();

4257 set_current_state(TASK_INTERRUPTIBLE);

4258 }

4259 __set_current_state(TASK_RUNNING);

4260return0;

The above code belongs to the migration service thread migration_thread, this thread constantly checks kthread_should_stop(),

It is not until kthread_should_stop() returns 1 that it can exit the loop, which means that as long as kthread_should_stop() returns 0, the process will sleep all the time. From the code we can see that checking kthread_should_stop() does not start until the state of the process is set to TASK_INTERRUPTIBLE. Therefore, if after a conditional check but another process tries to wake it up before schedule(), the wakeup operation of the process will not expire.

summary

From the above discussion, we can find that the key to avoid invalid wake-up of the process in Linux is to set the state of the process to TASK_INTERRUPTIBLE or TASK_UNINTERRUPTIBLE before the process checks the condition, and if the condition of the check is satisfied, it should be reset to TASK_RUNNING. . In this way, no matter whether the condition of the process is satisfied or not, the process will not enter the sleep state erroneously because it is moved out of the ready queue, thus avoiding the invalid wake-up problem.

PU Leather Flip Cover Shockproof Case

PU Leather  Flip Cover Shockproof Case,Pu Leather Case,Genuine Pu Leather Case,Best Premium Pu Leather Case

Guangzhou Jiaqi International Trade Co., Ltd , https://www.make-case.com