数组值计算公式是:weight = 1024 / 1.25nice 。
调度周期如果一个 CPU 上有 N 个优先级相同的进程 , 那么每个进程会得到 1/N 的执行机会 , 每个进程执行一段时间后 , 就被调出 , 换下一个进程执行 。如果这个 N 的数量太大 , 导致每个进程执行的时间很短 , 就要调度出去 , 那么系统的资源就消耗在进程上下文切换上去了 。
所以对于此问题在 CFS 中则引入了调度周期 , 使进程至少保证执行0.75ms 。调度周期的计算通过如下代码:
static u64 __sched_period(unsigned long nr_running){ if (unlikely(nr_running > sched_nr_latency)) return nr_running * sysctl_sched_min_granularity; else return sysctl_sched_latency;} static unsigned int sched_nr_latency = 8;unsigned int sysctl_sched_latency = 6000000ULL;unsigned int sysctl_sched_min_granularity = 750000ULL;当进程数目小于8时 , 则调度周期等于6ms 。当进程数目大于8时 , 则调度周期等于进程的数目乘以0.75ms 。
虚拟运行时间根据上面进程实际运行时间的公式 , 可以看出 , 权重不同的2个进程的实际执行时间是不相等的 , 但是 CFS 想保证每个进程运行时间相等 , 因此 CFS 引入了虚拟时间的概念 。虚拟时间(vriture_runtime)和实际时间(wall_time)转换公式如下:
vriture_runtime = (wall_time * NICE0_TO_weight) / weight
其中 , NICE0_TO_weight 代表的是 nice 值等于0对应的权重 , 即1024 , weight 是该任务对应的权重 。
权重越大的进程获得的虚拟运行时间越小 , 那么它将被调度器所调度的机会就越大 , 所以 , CFS 每次调度原则是:总是选择 vriture_runtime 最小的任务来调度 。
为了能够快速找到虚拟运行时间最小的进程 , Linux 内核使用红黑树来保存可运行的进程 。CFS跟踪调度实体sched_entity的虚拟运行时间vruntime , 将sched_entity通过enqueue_entity()和dequeue_entity()来进行红黑树的出队入队 , vruntime少的调度实体sched_entity排列到红黑树的左边 。

文章插图
如上图所示 , 红黑树的左节点比父节点小 , 而右节点比父节点大 。所以查找最小节点时 , 只需要获取红黑树的最左节点即可 。
相关步骤如下:
- 每个sched_latency周期内 , 根据各个任务的权重值 , 可以计算出运行时间runtime;
- 运行时间runtime可以转换成虚拟运行时间vruntime;
- 根据虚拟运行时间的大小 , 插入到CFS红黑树中 , 虚拟运行时间少的调度实体放置到左边;

文章插图
- 在下一次任务调度的时候 , 选择虚拟运行时间少的调度实体来运行 。pick_next_task 函数就是从从就绪队列中选择最适合运行的调度实体 , 即虚拟时间最小的调度实体 , 下面我们看下 CFS 调度器如何通过 pick_next_task 的回调函数 pick_next_task_fair 来选择下一个进程的 。

文章插图
pick_next_task_fair 会判断上一个 task 的调度器是否是 CFS , 这里我们默认都是 CFS 调度:

文章插图
update_currupdate_curr 函数用来更新当前进程的运行时间信息:
static void update_curr(struct cfs_rq *cfs_rq){ struct sched_entity *curr = cfs_rq->curr; u64 now = rq_clock_task(rq_of(cfs_rq)); u64 delta_exec; if (unlikely(!curr)) return; delta_exec = now - curr->exec_start; ------(1) if (unlikely((s64)delta_exec <= 0)) return; curr->exec_start = now; ------(2) schedstat_set(curr->statistics.exec_max, max(delta_exec, curr->statistics.exec_max)); curr->sum_exec_runtime += delta_exec; ------(3) schedstat_add(cfs_rq->exec_clock, delta_exec); curr->vruntime += calc_delta_fair(delta_exec, curr); ------(4) update_min_vruntime(cfs_rq); ------(5) account_cfs_rq_runtime(cfs_rq, delta_exec);}
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 黑客如何搭建和使用VMware和Kali Linux使用环境?
- 彻底搞懂Java线程池的工作原理
- 电脑鼠标右键菜单管理及彻底删除方法技巧
- Kali Linux,一个你欲罢不能的东西,非专业勿入
- Linux 命令总结
- 50个应知必会的Linux常识和操作
- 详细解析Linux /etc/passwd文件
- Linux系统安全攻防技术
- Linux 怎么修改最大文件打开数量?
- Linux经典面试题:网卡接收数据后,经过几次拷贝才能到用户进程
