Fully fair scheduling class is an example of scheduling class
static const struct Sched_class Fair_sched_class = {
. Next = &idle_sched_class,
. Enqueue_task = Enqueue_task_fair,
. Dequeue_task = Dequeue_task_fair,
. Yield_task = Yield_task_fair,
. Check_ Preempt_curr = Check_preempt_wakeup,
. Pick_next_task = Pick_next_task_fair,
. Put_prev_task = Put_prev_task_fair,
. Set_curr_task = Set_curr_task_fair,.
Task_tick = Task_tick_fair,
. Task_new = Task_new_fair,
};
A fully fair scheduling class or a function of a real-time scheduling class is invoked in the main scheduler and in the cycle scheduler according to the process type.
@next, point to the Idle scheduling class, and next to the real-time scheduling class points to the fully fair dispatch class. This order has been established before compiling and will not be created dynamically during system runtime.
@enqueue_task_fair, a new process is added to the ready queue, which occurs when the process changes from sleep state to a running state.
@dequeue_task, removes a process from the ready queue, which occurs when the process switches from a running state to a state that is not running. Although this is called a queue, the internal implementation is entirely determined by the Scheduler class.
@yield_task, a process can automatically discard execution through system call Sched_yield, at which point the kernel invokes the Yield_task function of the Scheduler class.
@check_preempt_curr, this function is called to preempt the current process, if necessary.
@pick_next_task, to select the next process that is about to run
@put_prev_task, call before using a different process to replace the currently running process
@set_curr_task, called when the scheduling policy of the current process changes, you need to call this function to change the current task of the CPU
@task_tick, called by the periodic scheduler each time the periodic scheduler is activated
@new_task, when the system creates a new task, it needs to be notified by this function to the Scheduler class
Data
An instance of the structure is embedded in each ready queue of the main scheduler:
230/* cfs-related fields in a runqueue/231 struct CFS_RQ {232 struct load_weight load;
233 unsigned long nr_running;
234
235 u64 Exec_clock;
236 u64 Min_vruntime;
237
238 struct rb_root tasks_timeline;
239 struct Rb_node *rb_leftmost; struct Rb_node *rb_load_balance_curr;
241/ * ' Curr ' points to currently running entity in this CFS_RQ.
242 * It is set to NULL otherwise (i.e when none are currently running).
243 * *
244 struct sched_entity *curr;
245
246 unsigned long nr_spread_over;
262};
Nr_running calculates the number of running processes on a queue, and load maintains the cumulative value of all of these processes. We'll compute the virtual clock by load.
Exec_clock only has the actual running time of statistics.
Min_vruntime calculates the minimum virtual run time for all processes on the queue.
Tasks_timeline is the tree node of the red and Black tree, which manages all processes, sorts them according to the virtual run time of these processes, and the leftmost process is the process with the smallest virtual run time and the process that is most needed to be invoked.
Rb_leftmost point to the leftmost node of the tree, we can actually find this node by traversing the Tasks_timeline.
Curr a dispatch entity that points to the current executable process in CFS_RQ
The working principle of CFS
The fully fair scheduling algorithm relies on the virtual clock to measure the CPU time that the waiting process can get in a perfectly fair system. However, there is no representation of the virtual clock in the data structure, because the virtual clock can be computed from the actual clock and the load weight associated with each process.
All calculations related to the virtual clock are performed in Update_curr, which are called in various places in the system.
336 static void Update_curr (struct CFS_RQ *cfs_rq) 337 {338 struct sched_entity *curr = cfs_rq->curr;
339 U64 now = rq_of (CFS_RQ)->clock; unsigned long delta_exec;
341
342 if (unlikely (!curr))
343 return ;
344 345/* 346 * Get the amount of "the" Current task is running
347 * Since the last Ti Me we changed load (this cannot
348 * overflow on \ Bits):
349
/delta_exec = (unsign Ed long) (Now-curr->exec_start);
351
352 __update_curr (CFS_RQ, Curr, delta_exec);
353 Curr->exec_start = now;
354
355 if (Entity_is_task (Curr)) {356 struct task_struct *curtask = task_of (curr);
357
358 cpuacct_charge (Curtask, delta_exec);
359 }
360}
339 rq_of (CFS_RQ)->clock The clock that implements the ready queue itself, and updates the clock value each time the periodic scheduler is invoked
Curr->exec_start saves the last time the load was changed, noting that it was not the last time the process was run. The current process may occur multiple times during one run Update_curr
__update_curr
304 static inline void 305 __update_curr (struct CFS_RQ *cfs_rq, struct sched_entity *curr, 306 unsigned long
Delta_exec) 307 {308 unsigned long delta_exec_weighted;
309 U64 Vruntime;
310 311 schedstat_set (Curr->exec_max, Max ((u64) delta_exec, Curr->exec_max));
312 313 Curr->sum_exec_runtime + = delta_exec;
Schedstat_add (Cfs_rq, Exec_clock, delta_exec);
315 delta_exec_weighted = delta_exec; 316 if (Unlikely (Curr->load.weight!= nice_0_load)) {317 delta_exec_weighted = Calc_delta_fair (delta_exec
_weighted, 318 &curr->load);
319} curr->vruntime + + = delta_exec_weighted; 321 322/* 323 * maintain cfs_rq->min_vruntime to being a monotonic increasing 324 * value tracking the
Leftmost vruntime in the tree. 325 */326 if (First_fair (CFS_RQ)) {327 vruntime = Min_vruntime (Curr->vruntime, 328 __pick_next_entity(CFS_RQ)->vruntime);
329} else Vruntime = curr->vruntime;
331 332 cfs_rq->min_vruntime = 333 Max_vruntime (cfs_rq->min_vruntime, vruntime);
334}
313 Sum_exec_runtime The CPU time cumulative value that the process consumes, @delta_exec is the difference between the last time the load statistic was updated, both of which are real.
316 if the priority of the process is (nice = 0), then the virtual time and physical time are the same, otherwise the virtual execution time is computed by calc_delta_mine
326 First_fait Detect if there is a leftmost node on the tree, that is, whether there are processes waiting on the tree for scheduling
332 Cfs_rq->min_vruntime is a monotonous increase.
During the running process, the vruntime of the process scheduling entity is increased monotonically, the higher the priority process, the slower the increase, so they move to the right speed is slower. The greater the chance of being dispatched.
Deferred tracking
The kernel has an inherent concept called scheduling latency, which guarantees that each process is at least one time interval to run.
sysctl_sched_latency
Parameters are used to control this behavior, and the default definition is 20ms, which can be controlled by/proc/sys/kernel/sched_latency_ns.
Sched_nr_latency
Controls the maximum number of activities processed during a delay period. If the data for the active process exceeds the upper limit, the delay cycle is also proportional to the linear extension.
__sched_period
The length of the delay period is usually sysctl_sched_latency, but if more processes are running, the value is calculated by using the following formula:
__sched_period = sysctl_sched_latency * nr_running/sched_nr_latency
During a delay period, the time of the delay period is allocated between the active processes by taking into account the weights of each process. For a given process that is represented by a dispatch entity, the allotted time is calculated as follows.
Static U64 sched_slice (struct Cfs_rq *cfs_rq, struct sched_entity *se)
{
U64 slice = __sched_period (cfs_rq-> nr_running);
Slice *= se->load.weight;
Do_div (Slice, cfs_rq->load.weight);
return slice;
}