2.2.1 核心概念 - 调度实体

简要介绍调度器内部的调度单元,即Sched Entity

想要研究调度器的工作原理,我们首先需要了解调度器的工作对象。

在相当长的一段时间内,Linux 都将 task_struct 作为调度器的工作对象。但这样做存在一个问题,特别是在CFS引入之后,对时间公平性的讨论让这个问题更加明显。

为了解决调度时时间分配的公平性问题,Linux 在2.16版本中引入了CFS(Complete Fair Scheduler). 早期的CFS实现只在进程层面上实现了时间的公平分配,例如,如果一个系统有100个进程,CFS会保证每个进程都获得1%的CPU时间,但实际上系统中的这100个进程可能隶属于两个用户A与B, 其中用户A拥有10个进程,用户B拥有90个进程,这种实现造成的结果是用户A只获得了10%的CPU时间,而用户B获得了90%的时间。如果用户B了解CFS的调度原理,那么他可以肆无忌惮地fork出更多的进程以攫取更多的CPU时间。可见CFS在进程层面上的公平,却导致了系统在用户层面上的不公平,甚至是漏洞。对于这种情况,一种更合理的策略是系统首先保证每个用户获得相同的时间,然后再对隶属于同一个用户的所有进程公平地分配该用户的时间。

将该概念进一步抽象,我们就得到了进程组的概念。进程组的引入实际上是增加了一个调度层级,调度器首先完成进程组的时间分配,再处理组内进程之间的时间分配,前文提到的用户分组只是进程组的一个特例。

为了简化调度器模型,内核单独抽象出了一个概念叫“调度实体”,用来封装调度对象, struct task_struct 与调度实体相关的字段包括:

/* file: include/linux/sched.h */
struct task_struct {
    /* 调度实体,调度器的调度对象,该字段用于 CFS */
    struct sched_entity se;
    /* 该字段用于 RT 调度器 */
    struct sched_rt_entity rt;
#ifdef CONFIG_CGROUP_SCHED
    struct task_group *sched_task_group;
#endif
    /* 该字段用于 DL 调度器 */
    struct sched_dl_entity dl;
}

三个表示调度实体的字段 se, rt, dl 分别用于不同的调度策略。下一节将介绍调度策略的概念,而调度实体的详细信息与用法会在讲解调度策略的实现章节详细介绍。

Last updated