1 .. include:: ../disclaimer-zh_CN.rst 1 .. include:: ../disclaimer-zh_CN.rst 2 2 3 :Original: Documentation/scheduler/sched-bwc.r 3 :Original: Documentation/scheduler/sched-bwc.rst 4 4 5 :翻译: 5 :翻译: 6 6 7 司延腾 Yanteng Si <siyanteng@loongson.cn> 7 司延腾 Yanteng Si <siyanteng@loongson.cn> 8 8 9 :校译: 9 :校译: 10 10 11 11 12 12 13 ============ 13 ============ 14 CFS 带宽控制 14 CFS 带宽控制 15 ============ 15 ============ 16 16 17 .. note:: 17 .. note:: 18 本文只讨论了SCHED_NORMAL的CPU带宽 18 本文只讨论了SCHED_NORMAL的CPU带宽控制。 19 SCHED_RT的情况在Documentation/scheduler 19 SCHED_RT的情况在Documentation/scheduler/sched-rt-group.rst中有涉及。 20 20 21 CFS带宽控制是一个CONFIG_FAIR_GROUP_SCHE 21 CFS带宽控制是一个CONFIG_FAIR_GROUP_SCHED扩展,它允许指定一个组或层次的最大CPU带宽。 22 22 23 一个组允许的带宽是用配额和周期 23 一个组允许的带宽是用配额和周期指定的。在每个给定的”周期“(微秒)内,一个任务组被分配多 24 达“配额”微秒的CPU时间。当cgroup 24 达“配额”微秒的CPU时间。当cgroup中的线程可运行时,该配额以时间片段的方式被分配到每个cpu 25 运行队列中。一旦所有的配额被分 25 运行队列中。一旦所有的配额被分配,任何额外的配额请求将导致这些线程被限流。被限流的线程将不 26 能再次运行,直到下一个时期的配 26 能再次运行,直到下一个时期的配额得到补充。 27 27 28 一个组的未分配配额是全局跟踪的 28 一个组的未分配配额是全局跟踪的,在每个周期边界被刷新为cfs_quota单元。当线程消耗这个带宽时, 29 它以需求为基础被转移到cpu-local“ 29 它以需求为基础被转移到cpu-local“筒仓”,在每次更新中转移的数量是可调整的,被描述为“片“(时 30 间片)。 30 间片)。 31 31 32 突发特性 32 突发特性 33 -------- 33 -------- 34 现在这个功能借来的时间是用于防 34 现在这个功能借来的时间是用于防范我们对未来的低估,代价是对其他系统用户的干扰增加。所有这些都 35 有很好的限制。 35 有很好的限制。 36 36 37 传统的(UP-EDF)带宽控制是这样的: 37 传统的(UP-EDF)带宽控制是这样的: 38 38 39 (U = \Sum u_i) <= 1 39 (U = \Sum u_i) <= 1 40 40 41 这既保证了每个最后期限的实现, 41 这既保证了每个最后期限的实现,也保证了系统的稳定。毕竟,如果U>1,那么每一秒钟的壁钟时间,我 42 们就必须运行超过一秒钟的程序时 42 们就必须运行超过一秒钟的程序时间,显然会错过我们的最后期限,但下一个最后期限会更远,永远没有 43 时间赶上,无边无界的失败。 43 时间赶上,无边无界的失败。 44 44 45 突发特性观察到工作负载并不总是 45 突发特性观察到工作负载并不总是执行全部配额;这使得人们可以将u_i描述为一个统计分布。 46 46 47 例如,让u_i = {x,e}_i,其中x是p(95)和 47 例如,让u_i = {x,e}_i,其中x是p(95)和x+e p(100)(传统的WCET)。这实际上允许u更小,提 48 高了效率(我们可以在系统中打包 48 高了效率(我们可以在系统中打包更多的任务),但代价是当所有的概率都一致时,会错过最后期限。然 49 而,它确实保持了稳定性,因为只 49 而,它确实保持了稳定性,因为只要我们的x高于平均水平,每一次超限都必须与低估相匹配。 50 50 51 也就是说,假设我们有两个任务, 51 也就是说,假设我们有两个任务,都指定了一个p(95)值,那么我们有一个p(95)*p(95)=90.25%的机 52 会,两个任务都在他们的配额内, 52 会,两个任务都在他们的配额内,一切都很好。同时,我们有一个p(5)p(5)=0.25%的机会,两个任务同 53 时超过他们的配额(保证最后期限 53 时超过他们的配额(保证最后期限失败)。在这两者之间有一个阈值,其中一个超过了,而另一个没有不足, 54 无法补偿;这取决于具体的CDFs。 54 无法补偿;这取决于具体的CDFs。 55 55 56 同时,我们可以说,最坏的情况下 56 同时,我们可以说,最坏的情况下的截止日期失败,将是Sum e_i;也就是说,有一个有界的迟延(在假 57 设x+e确实是WCET的情况下)。 57 设x+e确实是WCET的情况下)。 58 58 59 使用突发时的干扰是由错过最后期 59 使用突发时的干扰是由错过最后期限的可能性和平均WCET来评价的。测试结果表明,当有许多cgroup或 60 CPU未被充分利用时,干扰是有限的 60 CPU未被充分利用时,干扰是有限的。更多的细节显示在: 61 https://lore.kernel.org/lkml/5371BD36-55AE-4F7 61 https://lore.kernel.org/lkml/5371BD36-55AE-4F71-B9D7-B86DC32E3D2B@linux.alibaba.com/ 62 62 63 管理 63 管理 64 ---- 64 ---- 65 配额、周期和突发是在cpu子系统内 65 配额、周期和突发是在cpu子系统内通过cgroupfs管理的。 66 66 67 .. note:: 67 .. note:: 68 本节描述的cgroupfs文件只适用于cg 68 本节描述的cgroupfs文件只适用于cgroup v1.对于cgroup v2,请参阅Control Group v2。 69 :ref:`Documentation/admin-guide/cgroup-v2.r 69 :ref:`Documentation/admin-guide/cgroup-v2.rst <cgroup-v2-cpu>`. 70 70 71 - cpu.cfs_quota_us:在一个时期内补充 71 - cpu.cfs_quota_us:在一个时期内补充的运行时间(微秒)。 72 - cpu.cfs_period_us:一个周期的长度( 72 - cpu.cfs_period_us:一个周期的长度(微秒)。 73 - cpu.stat: 输出节流统计数据[下面进 73 - cpu.stat: 输出节流统计数据[下面进一步解释] 74 - cpu.cfs_burst_us:最大累积运行时间 74 - cpu.cfs_burst_us:最大累积运行时间(微秒)。 75 75 76 默认值是:: 76 默认值是:: 77 77 78 cpu.cfs_period_us=100ms 78 cpu.cfs_period_us=100ms 79 cpu.cfs_quota_us=-1 79 cpu.cfs_quota_us=-1 80 cpu.cfs_burst_us=0 80 cpu.cfs_burst_us=0 81 81 82 cpu.cfs_quota_us的值为-1表示该组没有 82 cpu.cfs_quota_us的值为-1表示该组没有任何带宽限制,这样的组被描述为无限制的带宽组。这代表 83 了CFS的传统工作保护行为。 83 了CFS的传统工作保护行为。 84 84 85 写入不小于cpu.cfs_burst_us的任何(有 85 写入不小于cpu.cfs_burst_us的任何(有效的)正值将配发指定的带宽限制。该配额或周期允许的最 86 小配额是1ms。周期长度也有一个1s 86 小配额是1ms。周期长度也有一个1s的上限。当带宽限制以分层方式使用时,存在额外的限制,这些在下 87 面有更详细的解释。 87 面有更详细的解释。 88 88 89 向cpu.cfs_quota_us写入任何负值都会移 89 向cpu.cfs_quota_us写入任何负值都会移除带宽限制,并使组再次回到无限制的状态。 90 90 91 cpu.cfs_burst_us的值为0表示该组不能 91 cpu.cfs_burst_us的值为0表示该组不能积累任何未使用的带宽。它使得CFS的传统带宽控制行为没有 92 改变。将不大于 cpu.cfs_quota_us 的任 92 改变。将不大于 cpu.cfs_quota_us 的任何(有效的)正值写入 cpu.cfs_burst_us 将配发未使用 93 带宽累积的上限。 93 带宽累积的上限。 94 94 95 如果一个组处于受限状态,对该组 95 如果一个组处于受限状态,对该组带宽规格的任何更新都将导致其成为无限流状态。 96 96 97 系统范围设置 97 系统范围设置 98 ------------ 98 ------------ 99 为了提高效率,运行时间在全局池 99 为了提高效率,运行时间在全局池和CPU本地“筒仓”之间以批处理方式转移。这大大减少了大型系统的全 100 局核算压力。每次需要进行这种更 100 局核算压力。每次需要进行这种更新时,传输的数量被描述为 "片"。 101 101 102 这是可以通过procfs调整的:: 102 这是可以通过procfs调整的:: 103 103 104 /proc/sys/kernel/sched_cfs_bandwidth_s 104 /proc/sys/kernel/sched_cfs_bandwidth_slice_us (default=5ms) 105 105 106 较大的时间片段值将减少传输开销 106 较大的时间片段值将减少传输开销,而较小的值则允许更精细的消费。 107 107 108 统计 108 统计 109 ---- 109 ---- 110 一个组的带宽统计数据通过cpu.stat 110 一个组的带宽统计数据通过cpu.stat的5个字段导出。 111 111 112 cpu.stat: 112 cpu.stat: 113 113 114 - nr_periods:已经过去的执行间隔的 114 - nr_periods:已经过去的执行间隔的数量。 115 - nr_throttled: 该组已被节流/限制的 115 - nr_throttled: 该组已被节流/限制的次数。 116 - throttled_time: 该组的实体被限流的 116 - throttled_time: 该组的实体被限流的总时间长度(纳秒)。 117 - nr_bursts:突发发生的周期数。 117 - nr_bursts:突发发生的周期数。 118 - burst_time: 任何CPU在各个时期使用 118 - burst_time: 任何CPU在各个时期使用超过配额的累计壁钟时间(纳秒)。 119 119 120 这个接口是只读的。 120 这个接口是只读的。 121 121 122 分层考虑 122 分层考虑 123 -------- 123 -------- 124 该接口强制要求单个实体的带宽总 124 该接口强制要求单个实体的带宽总是可以达到的,即:max(c_i) <= C。然而,在总体情况下,是明确 125 允许过度订阅的,以便在一个层次 125 允许过度订阅的,以便在一个层次结构中实现工作保护语义: 126 126 127 例如,Sum (c_i)可能超过C 127 例如,Sum (c_i)可能超过C 128 128 129 [ 其中C是父方的带宽,c_i是其子方 129 [ 其中C是父方的带宽,c_i是其子方的带宽。 ] 130 130 131 .. note:: 131 .. note:: 132 译文中的父亲/孩子指的是cgroup pa 132 译文中的父亲/孩子指的是cgroup parent, cgroup children。 133 133 134 有两种方式可以使一个组变得限流: 134 有两种方式可以使一个组变得限流: 135 135 136 a. 它在一段时期内完全消耗 136 a. 它在一段时期内完全消耗自己的配额 137 b. 父方的配额在其期间内全 137 b. 父方的配额在其期间内全部用完 138 138 139 在上述b)情况下,即使孩子可能有 139 在上述b)情况下,即使孩子可能有剩余的运行时间,它也不会被允许,直到父亲的运行时间被刷新。 140 140 141 CFS带宽配额的注意事项 141 CFS带宽配额的注意事项 142 --------------------- 142 --------------------- 143 一旦一个片断被分配给一个cpu,它 143 一旦一个片断被分配给一个cpu,它就不会过期。然而,如果该cpu上的所有线程都无法运行,那么除了 144 1ms以外的所有时间片都可以返回到 144 1ms以外的所有时间片都可以返回到全局池中。这是在编译时由min_cfs_rq_runtime变量配置的。这 145 是一个性能调整,有助于防止对全 145 是一个性能调整,有助于防止对全局锁的额外争夺。 146 146 147 cpu-local分片不会过期的事实导致了 147 cpu-local分片不会过期的事实导致了一些有趣的罕见案例,应该被理解。 148 148 149 对于cgroup cpu限制的应用程序来说, 149 对于cgroup cpu限制的应用程序来说,这是一个相对有意义的问题,因为他们自然会消耗他们的全部配 150 额,以及每个cpu-本地片在每个时期 150 额,以及每个cpu-本地片在每个时期的全部。因此,预计nr_periods大致等于nr_throttled,并且 151 cpuacct.用量的增加大致等于cfs_quota_u 151 cpuacct.用量的增加大致等于cfs_quota_us在每个周期的增加。 152 152 153 对于高线程、非cpu绑定的应用程序 153 对于高线程、非cpu绑定的应用程序,这种非过期的细微差别允许应用程序短暂地突破他们的配额限制, 154 即任务组正在运行的每个cpu上未使 154 即任务组正在运行的每个cpu上未使用的片断量(通常每个cpu最多1ms或由min_cfs_rq_runtime定 155 义)。这种轻微的突发只适用于配 155 义)。这种轻微的突发只适用于配额已经分配给cpu,然后没有完全使用或在以前的时期返回。这个突发 156 量不会在核心之间转移。因此,这 156 量不会在核心之间转移。因此,这种机制仍然严格限制任务组的配额平均使用量,尽管是在比单一时期更 157 长的时间窗口。这也限制了突发能 157 长的时间窗口。这也限制了突发能力,每个cpu不超过1ms。这为在高核数机器上有小配额限制的高线程 158 应用提供了更好的更可预测的用户 158 应用提供了更好的更可预测的用户体验。它还消除了在使用低于配额的cpu时对这些应用进行节流的倾向。 159 另一种说法是,通过允许一个片断 159 另一种说法是,通过允许一个片断的未使用部分在不同时期保持有效,我们减少了在不需要整个片断的cpu 160 时间的cpu-local 筒仓上浪费配额的可 160 时间的cpu-local 筒仓上浪费配额的可能性。 161 161 162 绑定cpu和非绑定cpu的交互式应用之 162 绑定cpu和非绑定cpu的交互式应用之间的互动也应该被考虑,特别是当单核使用率达到100%时。如果你 163 给了这些应用程序一半的cpu-core, 163 给了这些应用程序一半的cpu-core,并且它们都被安排在同一个CPU上,理论上非cpu绑定的应用程序有 164 可能在某些时期使用多达1ms的额外 164 可能在某些时期使用多达1ms的额外配额,从而阻止cpu绑定的应用程序完全使用其配额,这也是同样的数 165 量。在这些情况下,将由CFS算法( 165 量。在这些情况下,将由CFS算法(见CFS调度器)来决定选择哪个应用程序来运行,因为它们都是可运行 166 的,并且有剩余的配额。这个运行 166 的,并且有剩余的配额。这个运行时间的差异将在接下来的交互式应用程序空闲期间得到弥补。 167 167 168 例子 168 例子 169 ---- 169 ---- 170 1. 限制一个组的运行时间为1个CPU的 170 1. 限制一个组的运行时间为1个CPU的价值:: 171 171 172 如果周期是250ms,配额也是250 172 如果周期是250ms,配额也是250ms,那么该组将每250ms获得价值1个CPU的运行时间。 173 173 174 # echo 250000 > cpu.cfs_quota_us /* qu 174 # echo 250000 > cpu.cfs_quota_us /* quota = 250ms */ 175 # echo 250000 > cpu.cfs_period_us /* p 175 # echo 250000 > cpu.cfs_period_us /* period = 250ms */ 176 176 177 2. 在多CPU机器上,将一个组的运行 177 2. 在多CPU机器上,将一个组的运行时间限制为2个CPU的价值 178 178 179 在500ms周期和1000ms配额的情况下 179 在500ms周期和1000ms配额的情况下,该组每500ms可以获得2个CPU的运行时间:: 180 180 181 # echo 1000000 > cpu.cfs_quota_us /* q 181 # echo 1000000 > cpu.cfs_quota_us /* quota = 1000ms */ 182 # echo 500000 > cpu.cfs_period_us /* p 182 # echo 500000 > cpu.cfs_period_us /* period = 500ms */ 183 183 184 这里较大的周期允许增加突 184 这里较大的周期允许增加突发能力。 185 185 186 3. 将一个组限制在1个CPU的20%。 186 3. 将一个组限制在1个CPU的20%。 187 187 188 在50ms周期内,10ms配额将相当于1 188 在50ms周期内,10ms配额将相当于1个CPU的20%。:: 189 189 190 # echo 10000 > cpu.cfs_quota_us /* quo 190 # echo 10000 > cpu.cfs_quota_us /* quota = 10ms */ 191 # echo 50000 > cpu.cfs_period_us /* pe 191 # echo 50000 > cpu.cfs_period_us /* period = 50ms */ 192 192 193 通过在这里使用一个小的周期, 193 通过在这里使用一个小的周期,我们以牺牲突发容量为代价来确保稳定的延迟响应。 194 194 195 4. 将一个组限制在1个CPU的40%,并允 195 4. 将一个组限制在1个CPU的40%,并允许累积到1个CPU的20%,如果已经累积了的话。 196 196 197 在50ms周期内,20ms配额将相当于1 197 在50ms周期内,20ms配额将相当于1个CPU的40%。而10毫秒的突发将相当于1个 198 CPU的20%:: 198 CPU的20%:: 199 199 200 # echo 20000 > cpu.cfs_quota_us /* quo 200 # echo 20000 > cpu.cfs_quota_us /* quota = 20ms */ 201 # echo 50000 > cpu.cfs_period_us /* pe 201 # echo 50000 > cpu.cfs_period_us /* period = 50ms */ 202 # echo 10000 > cpu.cfs_burst_us /* bur 202 # echo 10000 > cpu.cfs_burst_us /* burst = 10ms */ 203 203 204 较大的缓冲区设置(不大于配额 204 较大的缓冲区设置(不大于配额)允许更大的突发容量。
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.