前言
我之前曾写过一篇关于CFS和CPU Cgroup的文章, 系统的分析了OS如何基于CFS实现对CPU资源的限制:
从CFS的层面来分析docker是如何限制容器对CPU的使用的
但是我们默认, 只有一个进程(准确的说是, 进程只有一个主线程), 不同的进程在不同的Cgroup中. 这是Docker的标准运行方式, 即同进程的线程一定是属于同一个Cgroup的. 但是如果同进程的线程在不同的Cgroup中, 会如何瓜分CPU呢? 本文将全面刨析这个问题, 这也是本文的最重要的部分.
此外,关于组调度的学习, 是参考了此博客, 我是在其基础之上做了总结, 因此并不会详细的讲组调度:cgroup原理简析:进程调度 – 不爱洗脸 – 博客园www.cnblogs.com/acool/p/6882644.html
此博文中有一个令人豁然开朗的配图, 我很喜欢:

关于组调度:
(以下未区分进程和线程)
- 根据优先级划分, 进程分为普通进程和实时进程

- 实时进程会抢占普通进程的CPU, 且当存在实时进程时, 实时进程将会被优先调度, 在实时进程没有执行完成之前,普通进程不会被调度. 在Linux的调度实现中, 存在两个调度类, 即实时调度类和CFS调度类, 前者使用FIFO或RR的调度算法, 后者使用CFS调度算法. 调度策略会先检查实时调度类的运行队列(即上图中的re_rq)是否为空, 即优先保证实时进程的运行.
- 不过无论是CFS还是RT, 其最终目标都是选择一个线程来执行, 但是在运行队列中排队的结构其实是一个调度实体(SE).
- 调度实体(SE)可能是一个线程,或者是一个调度组. 如果调度器选择的SE是一个线程, 那么选择的任务结束. 如果SE是一个调度组, 那么将在调度组中重新选择新的调度实体, 直到选中一个线程为止.
- Cgroup则对应了调度组. Root Cgrou则是最大的调度组, 每次调度都将从Root Cgroup中开始.
调度组的CPU资源划分
时间片的分配, 是根据SE进行的, 一个调度组的所有SE共享该调度组的CPU资源, 假设本机拥有4个core, Cgroup结构如下:

进程P1, P2 各包含4个线程, 其中P2的4个线程完全在Cgroup-4中, P2的4个线程, 以不同的比例分配在Cgroup-1和Cgroup-3中, 则P1, P2的CPU资源最终分配比例如下:
P1 在 Cgroup-1中的线程数 | P1 在 Cgroup-3中的线程数 | P1 : P2 的CPU占比 |
---|---|---|
0 | 4 | 1 : 1 |
1 | 3 | 5 : 3 |
2 | 2 | 3 : 1 |
3 | 1 | 5 : 1 |
4 | 0 | 2 : 1 |
我们这里以1 : 3的分配比分析:
- 首先C-1 比 C-2 的shares权重是2:1, 因此 C-1理论上应该占据4 * 2/3 个core
- 但是C-1 中只有一个线程, 因此C-1最终拥有1个core全部分配给P1
- C-3 和 C-4将平分C-2的3个core, 每人各拿 3/2个core, 分别分配给P1和P2
- 最终P1 拥有 1+ 3/2个core, P2拥有3/2个core