1. 前言
本专题我们开始学习进程管理部分。本文主要参考了《奔跑吧, Linux内核》、ULA、ULK的相关内容。本文只是作为学习笔记以用于构建知识框架,可能存在一些理解不恰当或不到位的地方,后续会随着学习的深入,逐步进行迭代。
本文主要记录system tick中断时进程调度部分的处理流程,注意此函数只是在判断符合条件时设置了TIF_NEED_RESCHED 标志,并没有真正执行进程切换。
kernel版本:5.10
平台:arm64
2. scheduler_tick
void scheduler_tick(void)
|--arch_scale_freq_tick();
|--sched_clock_tick()
|--update_rq_clock(rq);
|--thermal_pressure = arch_scale_thermal_pressure(cpu_of(rq));
|--update_thermal_load_avg(rq_clock_thermal(rq), rq, thermal_pressure);
|--curr->sched_class->task_tick(rq, curr, 0);//调用调度类对应的task_tick方法
| |--task_tick_fair(rq, curr, 0) //针对CFS调度类该函数是task_tick_fair
| |--for_each_sched_entity(se)
| entity_tick(cfs_rq, se, queued)
|--calc_global_load_tick(rq);
|--psi_task_tick(rq);
\--trigger_load_balance(rq);//触发SMP负载均衡
周期性调度是指Linux定时周期性地检查当前进程是否耗尽当前进程的时间片,也就是检查当前进程的实际运行时间是否超过理论计算的允许运行时间,并检查是否应该抢占当前进程。一般会在定时器的中断函数中,通过一层层函数调用最终到scheduler_tick()函数。
for_each_sched_entity: 此是一个宏定义for (; se; se = se->parent),顺着se的parent链表往上走 ,如果有一个se运行时间超过分配限额时间就需要重新调度。如果组调度未打开的情况下,这里就是一层循环
task_tick_fair:主要通过调用entity_tick来决定当前进程是否已经耗尽时间片,如果耗尽则需要设置TIF_NEED_RESCHED 标记
entity_tick(cfs_rq, se, queued)
|--update_curr(cfs_rq);//更新当前运行的调度实体的vruntime和就绪队列的min_vruntime
|--update_load_avg(cfs_rq, curr, UPDATE_TG);//Update task and its cfs_rq load average
|--update_cfs_group(curr);
|--if (cfs_rq->nr_running > 1)
| check_preempt_tick(cfs_rq, curr);
| |--ideal_runtime = sched_slice(cfs_rq, curr);//计算curr进程在本次调度周期中应该分配的(理论)时间片
| |--delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;//当前进程已经运行的实际时间
| |--if (delta_exec > ideal_runtime)//如果实际运行时间已经超过分配给进程的时间片
| | resched_curr(rq_of(cfs_rq));//需要抢占当前进程。设置TIF_NEED_RESCHED flag
| | clear_buddies(cfs_rq, curr);
| | return;
| |--if (delta_exec < sysctl_sched_min_granularity)//如果运行时间小于最小粒度时间,不应该抢占
| | return;
| |--se = __pick_first_entity(cfs_rq);//从红黑树中找到虚拟时间最小的调度实体
| |--delta = curr->vruntime - se->vruntime;
| |--if (delta < 0) return;//当前进程的虚拟时间仍然比红黑树中最左边调度实体虚拟时间小,不应该被抢占
| |--if (delta > ideal_runtime) //希望权重小的任务更容易被抢占?
| resched_curr(rq_of(cfs_rq));
check_preempt_tick():如果就绪队列就绪态的调度实体个数大于1需要检查是否满足抢占条件,如果可以抢占就设置TIF_NEED_RESCHED flag,表示当前进程可以被调度出去(触发抢占场景之一)。
delta_exec是当前进程已经运行的实际时间。如果实际运行时间已经超过分配给进程的时间片,自然就需要抢占当前进程。设置TIF_NEED_RESCHED flag。为了防止频繁过度抢占,我们应该保证每个进程运行时间不应该小于最小粒度时间sysctl_sched_min_granularity(0.75ms)。因此如果运行时间小于最小粒度时间,不应该抢占。从红黑树中找到虚拟时间最小的调度实体。如果当前进程的虚拟时间仍然比红黑树中最左边调度实体虚拟时间小,也不应该发生调度。这里把虚拟时间和实际时间比较,看起来很奇怪。感觉就像是bug一样,然后经过查看提交记录,作者的意图是:希望权重小的任务更容易被抢占。
from:http://www.wowotech.net/process_management/448.html
参考文档
- 奔跑吧,Linux内核
- ULK
- ULA