Explain in detail why the process needs to sleep?
Link: https://www.cnblogs.com/ydswin/p/18079436
A process is a part of the operating system
执行上下文
. To put it simply,
执行上下文
it includes the code to be executed and its related resources. The code to be executed is relatively easy to understand, which is the program code we write, for example:
int total = 10 + 20;
The code above assigns the result of adding 10 and 20 to
total
the variable , which is the code that the process will execute.
The resources related to the process include:
使用的内存
,
打开的文件
,
使用的CPU时间
and so on. The executed code and its related resources constitute
执行上下文
, also called
进程
. If people are compared to processes, then our daily behavior corresponds to the execution code, and the various social resources we have (such as money, real estate, cars, etc.) correspond to the resources occupied by the process.
Why do processes need to sleep?
Since the CPU is the main body of code execution, the code of the process needs to occupy CPU time. But sometimes the execution of the process requires some resources to provide data sources, and these resources may need to be obtained from the outside. For example, in a network program, the server needs to wait for the client to make a request before proceeding to the next step. But it is uncertain when the server will receive the client's request, and it may not receive the client's request for a whole day.
The server program can wait for the client's request in two ways:
忙等待
and
睡眠与通知
.
1. Busy Waiting
忙等待
It means to continuously detect whether the data is ready through an infinite loop, as shown in the following pseudo code:
for (;;) {
if (如果数据准备好) {
读取数据;
break;
}
}
As can be seen from the above code, busy waiting consumes a lot of CPU time to detect whether the data is ready.
Let's take an example from daily life
忙等待
. For example, when we eat out, when the restaurant is very busy, we may need to wait for a table. While waiting, we need to keep asking the waiter to understand the status of the vacant seats.
2. Sleep and wake-up
It can be seen that busy waiting is a very inefficient and time-wasting way. So is there a more efficient way? The answer is yes.
Let's take waiting for a table in a restaurant as an example. If the restaurant provides a device to notify guests, the waiter can use this device to notify the waiting guests when there is a vacancy. Then, the guests do not need to constantly ask the waiter about the vacancy status, and can take a nap while waiting.
Similarly, while the server program is waiting for a client request to arrive, the operating system can suspend the server process (sleep) and then execute other executable processes. When a client request arrives, the operating system wakes up the server process to handle the client request.
As shown in the following figure:
When
进程M
is waiting for some resources in the system to become ready, the operating system will
进程M
switch out of the CPU and put
进程M
into the sleep queue. Then the operating system will select the most suitable process (as shown in the figure
进程1
) from the runnable queue and schedule it to the CPU to run.
When
进程M
the waiting resource becomes ready, the operating system will put
进程M
it back into the runnable queue. In this way,
进程M
it can compete for the CPU's running time in the next scheduling cycle. As shown in the following figure:
In the above figure, when a client request arrives, the operating system wakes up the waiting client request
进程M
and puts it back into the runnable queue.
This is the sleep and wake-up mechanism in the operating system.
Implementation of sleep and wake-up mechanism of Linux
In the Linux kernel, many system calls and kernel functions may cause a process to sleep, such as I/O-related system calls,
sleep
kernel-like functions, memory allocation functions, etc.
Next, we will use
sleep
the kernel functions of the class to analyze how Linux implements the sleep and wake-up mechanism.
1. Use of sleep function
In the Linux kernel, if you want to put a process into sleep mode, you can call
schedule_timeout_interruptible()
the kernel function. Its prototype is as follows:
signed long __sched schedule_timeout_interruptible(signed long timeout);
Parameters
timeout
indicate how long you want the process to sleep. This function will make the process sleep
timeout
for ticks. For example, if you want the process to sleep for 1 second, you can use the following code to achieve it:
...
schedule_timeout_interruptible(1 * HZ);
// 1秒后进程被唤醒,继续执行下面代码
...
2. Implementation of sleep function
Next, let's analyze
schedule_timeout_interruptible()
how the kernel function puts the process into sleep mode. The code is as follows:
signed long __sched schedule_timeout_interruptible(signed long timeout)
{
__set_current_state(TASK_INTERRUPTIBLE);
return schedule_timeout(timeout);
}
schedule_timeout_interruptible()
The kernel function mainly does the following two things:
-
Call
__set_current_state()
the function to set the process to be interruptible中断睡眠状态
. It should be noted that this step only sets the state of the process to an interruptible sleep state, but the process has not yet been removed from the CPU by the kernel scheduler. -
Calling
schedule_timeout()
the function makes the process actually enter the sleep state (give up the right to use the CPU).
From the above code, we can see that
schedule_timeout()
the function is the main body that puts the process to sleep. Then, let's continue to analyze
schedule_timeout()
the implementation of the function:
signed long __sched schedule_timeout(signed long timeout)
{
struct timer_list timer;
unsigned long expire;
...
expire = timeout + jiffies;
// 1. 将当前进程添加到定时器中,定时器的超时时间为expire,回调函数为process_timeout
setup_timer_on_stack(&timer, process_timeout, (unsigned long)current);
__mod_timer(&timer, expire, false, TIMER_NOT_PINNED);
// 2. 主动触发内核进行进程调度
schedule();
// 3. 将进程从定时器中删除
del_singleshot_timer_sync(&timer);
destroy_timer_on_stack(&timer);
timeout = expire - jiffies;
out:
return timeout < 0 ? 0 : timeout;
}
schedule_timeout()
The logic of the function is mainly divided into the following three steps:
-
Add the current process to the timer, set the timeout of the timer to
expire
, and the callback function toprocess_timeout()
. Then when the timer times out, it will trigger the call ofprocess_timeout()
the function. -
Calling
schedule()
the function triggers the kernel to schedule the process. Since the current processschedule_timeout_interruptible()
is set to in the function可中断睡眠状态
, when the scheduler finds that the current process is可中断睡眠状态
, it will remove the current process from the runnable queue and give up the right to use the CPU. -
When the process is awakened, it will be deleted from the timer.
From the above analysis, we can see that when calling
schedule_timeout()
the function, the kernel will create a timer for the current process, and its timeout period is set to
schedule_timeout()
the parameter passed by the function plus the current time. When the timer expires, it will trigger the call of
process_timeout()
the function, and
process_timeout()
the function will eventually call
try_to_wake_up()
the function to wake up the process.
Let's analyze
try_to_wake_up()
the implementation of the function and see how it wakes up the process:
static int
try_to_wake_up(struct task_struct *p, unsigned int state, int wake_flags)
{
unsigned long flags;
int cpu, success = 0;
...
// 1. 为进程挑选一个最合适的 CPU 运行
cpu = select_task_rq(p, p->wake_cpu, SD_BALANCE_WAKE, wake_flags);
...
// 2. 把进程添加到 CPU 的可运行队列中
ttwu_queue(p, cpu);
...
return success;
}
In the above code, we only keep the core code. It can be seen that
try_to_wake_up()
the function mainly completes 2 things:
-
Call
select_task_rq()
the function to select the most suitable CPU for the process to run. -
Calling
ttwu_queue()
the function adds the process to the CPU's runnable queue.
After the awakened process is added to the CPU's runnable queue, it will not be executed immediately. The kernel will select a suitable process for scheduling in the next scheduling cycle, and the awakened process may be selected to run.
Summarize
This article mainly introduces why processes need to have sleep and wake-up functions, and analyzes the implementation principles of process sleep and wake-up. The process sleep and wake-up functions are mainly to solve the problem that processes need to constantly detect the status of resources when waiting for certain resources to become available. This detection not only wastes precious CPU time, but also affects the system throughput.
Process sleep can actively give up the right to use the CPU when the process is waiting for resources to become available, so that the CPU can run other executable processes, thereby achieving optimal CPU utilization. When the resources that the process is waiting for become available, the kernel actively wakes up the waiting process and the process can continue to run.
Autumn
The recruitment has already begun. If you are not well prepared,
Autumn
It's hard to find a good job.
Here is a big employment gift package for everyone. You can prepare for the spring recruitment and find a good job!