2. Scheduling Algorithm
The kernel Scheduling Algorithm of linux2.4 is easy to understand: during each process switchover, the kernel scans every process in the ready queue in sequence to calculate the priority of each process, select the process with the highest priority to run. Although this algorithm is easy to understand, the time it takes to select the process with the highest priority cannot be ignored. The more ready processes in the system, the more time it takes, and the time complexity is O (n ).
The O (1) algorithm adopted by the 2.6 kernel solves this problem well. From its name, we can see that no matter how many processes can be run in the system, this algorithm can always select the process with the highest priority within a limited period of time.
2.1 operational queue
Each time a scheduler switches a process, it selects the best process in the ready queue to run. The Linux kernel uses the runqueue data structure (which is RQ in the latest kernel) to indicate a runable Queue (that is, a ready queue). Each CPU has only one such structure. This structure not only describes the chain list of processes in the runable state (task_running) in each processor, but also describes the scheduling information of the processor. The following describes some fields in the structure in detail.
Spinlock_t lock: Protection of spin locks of process linked lists;
Unsigned long nr_running: Number of processes in the running queue linked list;
Unsigned long nr_switches: The number of times the CPU executes the process switch;
Unsigned long nr_uninterruptible: The total number of processes that were previously running in the queue linked list and are now in sleep state;
Unsigned long expired_timestamp: The time when the oldest process in the expired queue is inserted into the queue;
Unsigned long timestamp_last_tick: The time of the last timer terminal;
Task_t * curr: Indicates the process descriptor of the processes currently running on the local CPU, that is, current;
Task_t * idle: Pointer to the idle process descriptor on the local CPU;
Struct mm_struct * prev_mm: The address used to store the memory descriptor of the replaced process during process switching;
Prio_array_t * active: Pointing to the active linked list in the runable queue;
Prio_array_t * expired: Pointing to the expired linked list in the runable queue;
Prio_array_t arrays [2]: The elements in this array represent the active process set and the expired process set in the running queue respectively;
Int best_expired_prio: The process with the highest priority in the expired process;
So far, you may not have a deep understanding of the above fields. The best way is to study the following content and then look back at the purpose of these fields. As we have mentioned above, the main function of the runqueue structure is to describe the linked list composed of processes in a running state. However, the so-called runqueue does not connect the runqueue structure of some columns, but is embodied by the arrays array in the runqueue structure. The elements of this array are of the prio_array_t type.
2.2 priority Array
Another core data structure of the O (1) algorithm is the prio_array structure. This struct contains an array queue that represents the process priority. It contains the linked list formed by each priority process.
1 |
#define MAX_USER_RT_PRIO 100 |
2 |
#define MAX_RT_PRIO MAX_USER_RT_PRIO |
3 |
#define MAX_PRIO (MAX_RT_PRIO + 40) |
4 |
typedef struct prio_array prio_array_t; |
6 |
unsigned int nr_active; |
7 |
unsigned long bitmap[BITMAP_SIZE]; |
8 |
struct list_head queue[MAX_PRIO]; |
Because the maximum process priority is 139, the maximum value of max_prio is 140 (specifically, normal processes use the priority from 100 to 139, and real-time processes use the priority from 0 to 99 ). Therefore, the queue array contains 140 processes in the running state. The processes on each list of priorities have the same priority, processes on different process linked lists have different priorities.
In addition, the prio_array structure also includes a priority bitmap. The bitmap uses a single bit to represent a priority. The first priority must contain at least five 32 bits. Therefore, the bitmap_size value is set to 5. At first, all the bits in the bitmap are set to 0. When a priority process is in a running state, the bit corresponding to this priority is set to 1.
Therefore, the highest priority of the search system in the O (1) algorithm is converted to the first bit set to 1 in the Search priority bitmap. Compared with the 2.4 kernel, the priority of each process is different. Because the number of process priorities is a fixed value, the time for finding the best priority is constant, it is not affected by the number of executable processes as in the previous method.
If the priority is determined, it is easy to select the next process. You only need to select a process on the linked list corresponding to the queue array.
2.3 active and expired Processes
In the operating system principle class, we know that when a running process runs out of time slice, it will be in the ready state, in this case, the scheduler selects a process from the ready process as the process to be run.
In a Linux kernel, the ready and running states are called the task _ running ). For processes that are running in the system, we can divide them into three types: Processes in the execution status, and processes in the execution status, some processes in the runable State have not used up their time slice, and they are waiting to be run. The remaining processes have used up their time slice, before other processes use up their time slices, they cannot run any more.
Based on this, we divide processes into two categories: active processes, those that have not used up the time slice; expired processes, those that have used up the time slice. Therefore, the scheduler selects a process with the best priority in the active process set. If the process time slice is used up, the process is placed in the expired process set.
In the runable queue structure, the two elements of the arrays array are used to represent the active process set and the expired process set described earlier. The active and expired pointers direct to the two sets respectively.
When the time slice of all processes is used up, the scheduler will re-allocate the time slice for each process. 2.4 The process for allocating time slices in the kernel depends on the number of processes that can be run. The specific method is to traverse each process in sequence and assign a new time slice through its priority. The allocation time slice in the O (1) scheduling algorithm is much simpler. Processes in the expiring process set and the active process set are exchanged. Therefore, the time taken by the "re-computing" time slice in the O (1) algorithm is a constant and does not depend on the number of processes that can be run.
For the relationship between the runable queue and two priority arrays, see the figure below:
As analyzed above, the runqueue structure and priority array structure allow the Q (1) scheduling algorithm to be completed within a limited time, and it does not depend on the number of processes that can be run in the system.
Refer:
1. Have a deep understanding of the Linux kernel (the third edition); (US) Bove, sister; translated by Zhang Hongwei, Zhang Qiong, Chen Lijun; China Power Press;
2. Linux kernel design and implementation; translated by love, R., Chen Lijun; published by Mechanical Industry Press;
3. http://edsionte.com/techblog/archives/2851