Transferred from
Http://hi.baidu.com/mychaoyue2011/blog/item/6df45895c3d63243d0135e01.html
Analysis of this reportLinux2.6 kernel Scheduling Policies and algorithms.LinuxThe kernel supports multiple CPUs, and each CPU executes a similar policy. However, when necessary (for example, when the CPU load is not balanced), higher inter-CPU scheduling will be performed, this will also lead to issues such as CPU synchronization. To simplify the problem, we will only discuss the situation of a single CPU.
LinuxThe scheduling object in is a task, which is still called a process. Each task has the following five states:
1. task_running: It indicates that the process is not blocked, that is, the combination of ready and running. As to whether it is ready or running, you can make the following judgment when necessary: the scheduler obtains the Process status of the process as task_running and the current_task of the CPU is the process, it is regarded as running, otherwise it is ready. There are not many situations that need to be judged above, so it is sufficient to describe it with task_running.
2. task_interruptible: the process is in the block state and can be awakened by signal.
3. task_uninterruptible: the process is in the block state but cannot be awakened by signal.
4. task_zombie: the process has ended, but the descriptor has not been recycled.
5. task_stopped: The process stops running. It usually occurs when receiving signals such as sigstop, sigtstp, sigttin, and sigttou, or when debugging, any signal is received. You can send sigcont to it to continue running.
The task status is recorded in the State member of the task_struct structure.
1. Scheduling Policy
LinuxThe process scheduling policy is based on priority scheduling, that is, the process with the highest priority is prioritized. Based on priority scheduling, processes can be divided into real-time processes (soft real-time in real time) and general processes through the assigned priority range. Real-time processes take precedence over general processes, and special scheduling policies are used to ensure their (soft) Real-time performance.
· Priority system
The priority of all processes in the system is between [0 .. MAX_PRIO-1], and the lower the value, the higher the priority. The priority range of real-time processes is [0 .. MAX_RT_PRIO-1], the general process priority in [max_rt_prio..max_prio]. the default configuration in the current kernel is: The process priority is in [0 .. 139], in which the real-time process occupies [0 .. 99]. Generally, the process occupies [100 .. 139].
The priority of a real-time process has been fixed since its inception and will not change, so as to ensure that a real-time process with a given priority can always seize a process with a lower priority than it.
Generally, the priority of a process is static or dynamic. The static priority is determined when the process is generated, while the dynamic priority dynamically changes with the Process status during running.
Their static priority is determined directly by the Nice value: static_prio = max_rt_prio + nice + 20. The nice value range is [-20... 19].
However, their dynamic priority is calculated using the effective_prio function, and the highly interactive priority is calculated based on the static priority, and the highest-5 additional priority is obtained, however, if the CPU usage is high, the priority of up to + 5 will be deducted, but the adjusted priority must be kept at [100 .. 139:
Static int inclutive_prio (task_t * P)
{
Int bonus, PRIO;
If (rt_task (p)/* if it is a real-time process */
Return p-> PRIO;/* No adjustment */
Bonus = current_bonus (P)-max_bonus/2;/* calculate the reward value */
Prio = p-> static_prio-bonus;/* reward */
If (PRIO <max_rt_prio)/* prevents out-of-bounds priority */
Prio = max_rt_prio;
If (PRIO> MAX_PRIO-1)
Prio = MAX_PRIO-1;
Return PRIO;
}
The current_bouns macro root scales the sleep_avg of the current process proportionally to its reward priority:
# Define current_bonus (p )\
(Ns_to_jiffies (P)-> sleep_avg) * max_bonus /\
Max_sleep_avg)
Sleep_avg of a process records the time when a process is used for sleep and execution, ranging from 0 to ns_max_sleep_avg (ns_max_sleep_avg is different from max_sleep_avg. The former is in the unit of NS, while the latter is in the unit of Jiffy, which is generally 5 ms. the unit of sleep_avg is ns. The default value of sleep_avg is 10 ms (2 Jiffy ). its value increases at the end of the process sleep according to the sleep time, and the running time decreases more when the process is running.
3 Linux kernel scheduling policy and algorithm analysis
· Process sleep and awakening
The sleeping process is in the status of task_interruptible or task_uninterruptible, and does not participate in scheduling. wait for something to happen. When the waiting condition may occur, the system will call try_to_wake_up () to wake up the process, that is, place the Process status in task_running and re-schedule it. For task_interruptible, there may also be situations where the signal is received and it is awakened in advance.
· Description of the effect of policies on interactive processes
At first glance, we can see that this policy is somewhat strange about the optimization of interaction processes. It increases the responsiveness of interaction jobs obviously, but does it make non-interaction processes hungry? Interactive jobs enjoy too many privileges: More running time slices, higher priority, and even remain active when the time slice is used up. However, all of the above ignore an important fact: it is an interactive job that will often sleep without being involved in scheduling. Therefore, this time slice can be used up several times before it can be used up. When the interaction process ends and begins to become greedy, the dynamic priority calculation will deprive it of the preceding privileges and add it to the ranks of non-interactive processes. The layer-2 rotation of interaction processes in the above strategy can also enable more interaction processes to sleep as early as possible, so as to leave time for non-interaction processes to use. From this we can see that through the above policy
This improves the system response without losing the fairness and effectiveness of the policy.
· Differences between two-layer time slice rotation in the Policy
The first layer of time slice rotation is used to calculate the time slice based on the dynamic priority of the process. For inactive processes, it can control their CPU usage relative to other processes (and for non-interactive processes, there is no second-level rotation); second-level time slice rotation as described above, mainly to make more interactive processes sleep as soon as possible. The roles of the two are different, but since the latter is included in the former, the latter is called a second-layer rotation (this word is self-prepared and I don't know if it is appropriate, the first and second types of rotation may be better. The above explanation is just my understanding ).
2. Scheduling Algorithm
The Linux 2.6 kernel implements a set of O (1) scheduling algorithms, that is, the time required for each scheduling is independent of the total number of processes in the CPU. The complexity of O (n) is much more efficient and sophisticated than the previous kernel Scheduling Algorithm in Linux. In addition, the real-time process can be fully guaranteed. Imagine that before a real-time process is scheduled, the schedule function re-calculates the time slice, which takes O (n) Time to complete. There are many expired processes in the system, which lead to n very large, the real-time process may have been running for a long time. Currently, the time spent on each scheduling is almost the same, and the real-time process will be quickly scheduled and put into operation once necessary.
First, we will introduce several data structures used to implement the algorithm in several kernels, as well as some of their core algorithms:
· Prio_array
Linux defines a struct prio_array:
Struct prio_array {
Unsigned int nr_active;/* Total number of currently active processes */
Unsigned long bitmap [bitmap_size];/* bitmap of active processes */
Struct list_head queue [max_prio];/* an array composed of header pointers of each priority queue */
};
This struct provides information about all processes in the system that can be scheduled. The second member bitmap is very interesting. It is a binary string (B0, B1 ,... BN). When bi = 1, an active process exists in the queue [I] with priority I. Therefore, the first one enables Bi = 1 to correspond to the highest priority in the active process. The process must be in queue [I. (Todo: add some reason), so when the scheduler schedule () selects the next running process, you only need to obtain the first process in queue [I] to run it:
Idx = sched_find_first_bit (array-> Bitmap);/* obtain the first I, S. T. BI = 1 */
Queue = array-> queue + idx;/* equivalent to queue [I] */
Next = list_entry (queue-> next, task_t, run_list);/* Get the first member */
This enables the preceding priority scheduling policy to select the next active process.
For comparison, let's take a look at this section in the Linux 2.4 kernel scheduling algorithm to select the cycle of the next process:
Next = idle_task (this_cpu);/* default process */