CFS带宽控制浅析

摘要

    什么是带宽控制?简单来讲就是控制一个任务组在给定周期时间内可消耗的CPU时间,如果在周期内消耗的CPU时间超额,则限制这个任务组内的任务调度,直到下一个周期。

    本文要讨论的CFS带宽控制,它是专门针对CFS调度策略而言的;相应的,对于RT调度策略还有RT带宽控制。

 

原理

    CFS带宽控制是基于公平调度任务组实现的,因而使用时需要打开CONFIG_FAIR_GROUP_SCHED内核配置;一个任务组通过运行队列cfs_rq组织本组内的任务(实际上是调度实体),每个CPU在组内都有一个对应的cfs_rq运行队列。

 

带宽相关参数

    我们通常意义上所讲的带宽概念是 带宽=fun{数量,周期},这和CPU带宽概念也是符合的。任务组的带宽由两个因素决定:quota和period。其中,period是周期(单位us),而quota则是任务组在周期内的可用CPU时间。一旦确定了quota和period,一个任务组的CPU带宽也就确定了。

    CFS带宽控制的关键就是要控制任务的运行时间,若任务组内在周期内消耗的CPU时间超过了quota,内核则为组内的运行队列设置一个“throttled”标志,以表示此队列在本周期限制调度,直到下一个周期才能得以恢复。

    除了这两个参数外,还有几个参数需要说明:

    runtime:表示任务组周期内剩余的可运行时间
    runtime_expires:周期到期时间,也就是deadline
    runtime_remaining:一个队列上剩余的可运行时间

    参数runtime是任务组的剩余CPU时间,它由组内各个cfs_rq运行队列所共享。理论上一个周期结束时它的值将重新置为quota,但是为了性能方面考虑,内核实际只在在任务组中有运行队列发生“throttled”才去更新任务组的runtime_expires和runtime。

    任务组组内各个cfs_rq上的任务对CPU时间的需求量不一致,为了保证各个cfs_rq上的CPU时间分配的相对合理(即不浪费,又不太频繁的申请),各个CPU上的cfs_rq每次只从runtime中申请“一小片”时间。这个“时间片”可以通过/proc/sys/kernel/sched_cfs_bandwidth_slice_us(默认值为5ms)接口来调整。

    Slice取值的大小随需求而定:slice越大各个cfs_rq每次可运行的时间越长,在总的runtime有限的情况下一个周期内分配slice的频率就越低;而slice值越小,各个cfs_rq的时间粒度也越小,这样可以更精确的分配给各个cfs_rq运行队列的时间。

    CGROUP的cpu子系统为CFS带宽控制提供了一些接口文件,用于调整任务组的带宽控制参数。

 

带宽控制参数的cgroup接口
-----------------------

参数quota与period可通过cgroup中的cpu子系统来进行控制。

cpu.cfs_quota_us: 时间份额。周期内可用时间(微秒),默认值是-1,即没有限制
cpu.cfs_period_us: 周期时间(微秒)。默认值是100000,即100ms
cpu.stat: 带宽限制状态相关信息(后面有更详细介绍)。

下面来详细了解一下这些参数的含义:

    参数cpu.cfs_quota_us为-1表示这个任务组没有带宽限制,早期的CFS调度中采用的就是这种无带宽限制方式。

    往cpu.cfs_quota_us文件写入正值将设置此任务组的CPU带宽限制,写入的值即为任务组的quota;而向cpu.cfs_quota_us写入负数值则是取消任务组的带宽限制;如果任务组已经处于“throttled”限制状态,则重新设置任务组的带宽会消除掉它的“throttled”状态,让任务组重回非调度限制状态。

    任务组的quota和period的最小值是1ms,所以往cpu.cfs_quota_us和cpu.cfs_period_us写入的正数不能小于1000;而period的最值为1s,即cpu.cfs_period_us最大值为1000000。除了这些限制外,CFS带宽控制中的参数还受到任务组层级的影响,这些将在稍后更详细的讲述。

 

查看CFS带宽控制信
-----------------------

任务组的CFS带宽控制相关的信息可通过cgroup中的(cpu子系统)的cpu.stat文件接口查询,它是一个只读文件。

cpu.stat:
- nr_periods: 目前为止经历多少个周期
- nr_throttled: 任务组发生带宽受限的次数
- throttled_time: 任务组中调度实体总的受限时间


任务组层级的限制
-----------------------

    前面提到的cgroup的cpu.cfs_quota_us和cpu.cfs_period_us接口可以将一个任务组的带宽控制在:max(c_i) <= C 。其中,C表示parent任务组的带宽;而c_i表示某个子组的带宽,即子组中最大带宽不能超过parent组的带宽。但是允许子组设置的带宽总额大于parent层级的带宽,即:Sum (c_i) >= C。

    任务组进入到调度限制状态的情况有两种:

  1. 任务组在一个period内用完自己的quota

  2. parent任务组在它的周期内用完quota

    在上面的情况2)中,即使子组还有剩余的runtime也会限制调度,直到parent重新分配到新的runtime。


相关示例
-----------------------

    为了方便讲述,我将周期内一个cpu所能提供的计算资源称作一个cpu时间;例如在一个4核系统中,一个周期内这个系统总共可以提供4个cpu时间。

1. 任务组的带宽设置为%100

 # echo 250000 > cpu.cfs_quota_us /* quota = 250ms */
 # echo 250000 > cpu.cfs_period_us /* period = 250ms */

上面的设置可以为任务组在周期内提供1个cpu的带宽资源,即总的cpu占用率为100%。

2. 任务组的带宽配置为200%

# echo 1000000 > cpu.cfs_quota_us /* quota = 1000ms */
# echo 500000 > cpu.cfs_period_us /* period = 500ms */

    上面的配置可以为任务组在周期内提供2个cpu的带宽资源,即周期内总的cpu占用率总和最多可达到200%。
  在此带宽比例配置前提下,周期越长,任务组的吞吐量越大。

3. 任务组的带宽为限制为%20

    # echo 10000 > cpu.cfs_quota_us /* quota = 10ms */
    # echo 50000 > cpu.cfs_period_us /* period = 50ms */

    上面的配置限制任务组在周期内只能提供20%的cpu带宽资源,即周期内总的cpu占用率总和不能超过20%。在这种带宽比例的情况下,周期越短响应延迟也就越小。

参考:Documentation/scheduler/sched-bwc.txt

你可能感兴趣的:(linux内核)