> For the complete documentation index, see [llms.txt](https://s3.shizhz.me/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://s3.shizhz.me/linux-sched/cfs-sched/logic-period.md).

# 2.6.2.3 调度逻辑 - 调度周期

从CFS的调度原理来考虑的话，CFS似乎不需要调度周期的概念，因为CFS并不是预先给任务分配时间片，而是根据大家当前的运行时间来判断谁应该是下一个该执行的任务，这样所有任务都随着时间的推进齐头并进。但为了用来调度延迟，CFS也需要引入调度周期。

什么是调度延迟呢？CFS不仅需要保证时间分配的公平，还要保证各个任务每隔一段时间就能够执行一次，一个任务在两次被调度到的时间间隔就是调度延迟。相反，调度器还需要保证任务在每次得到机会执行时，除了任务主动放弃CPU, 尽量不要太快地被踢出来，因为太频繁的上下文切换会导致系统的总体性能降低。所以 CFS 没有使用固定的时间长度作为调度周期，而是根据当前队列中的任务数量动态计算出调度周期的长度，该逻辑由函数 `__sched_period` 实现：

```
/* file: kernel/sched/fair.c */
/* 参数 nr_running 表示当前 cfs_rq 中的任务总数 */
static u64 __sched_period(unsigned long nr_running) {
/* sched_nr_latency: 8 */
if (unlikely(nr_running > sched_nr_latency))
/* sysctl_sched_min_granularity: 0.75ms */
return nr_running * sysctl_sched_min_granularity;
else
/* sysctl_sched_latency: 6ms*/
return sysctl_sched_latency;
}
```

当队列中所有的任务超过8个时，CFS的调度周期为任务总数乘以0.75ms，否则调度周期为固定的6ms, 这样可以保证任务的切换频率比较合理。

算出调度周期之后，系统还需要为任务计算其在一个调度周期内的时间配额，函数 `sched_slice` 用来实现该逻辑：

```
/* file: kernel/sched/fair.c */
static u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se) {
unsigned int nr_running = cfs_rq->nr_running;
u64 slice;

/* 调度周期 */
slice = __sched_period(nr_running + !se->on_rq);

/* 暂时不考虑组调度，此处的循环只会执行一次 */
for_each_sched_entity(se) {
struct load_weight *load;
struct load_weight lw;

cfs_rq = cfs_rq_of(se);
/* 整个运行队列 cfs_rq 的总权重 */
load = &cfs_rq->load;

/* se->load.weight为se的权重，调用函数__calc_delta得到slice*se->load.weight/load.weight,
    * 即根据 se 在整个队列中的权重比例分配时间 */
slice = __calc_delta(slice, se->load.weight, load);
}

if (sched_feat(BASE_SLICE))
slice = max(slice, (u64)sysctl_sched_min_granularity);

return slice;
}
```

当任务在当前调度周期内的耗时超过自己的配额时，调度器就会将其踢出去，换其他任务来执行，下一节将会详细讨论该逻辑。
