cgroup 管理cpu资源

一个功能完备的资源隔离方案需要有以下功能支持:

  1. 对用户态提供对应接口,使用户态能直接控制内核。
  2. 对进程有效隔离,同时能控制进程的生命周期。
  3. 对资源进行有效分配控制,常用的资源有CPU时间,内存,IO,网络等设备。
  4. 对资源使用情况能有效记录。

对于用户态接口,cgroup设计了cgroup文件系统,使用户态通过文件读写将控制信息传递给内核(一切即文件的思想)。
进程隔离则是使用了进程组和namespace配合搞定
资源控制分配则是根据内核现有的控制机制,将用户态控制参数传递给内核对应的点,具体后续写。
资源的记录则是内核变化在用户态展现,比较简单。

     cgroup类似与进程,他们是分等级的,各个属性都是从父进程哪里继承过来。cgroup包含了多个孤立的子系统,没一个子系统代表一个单一的资源。rhel6中一共准备了9个子系统,以下是每个子系统的详细说明:
      1)blkio 这个子系统设置限制每个块设备的输入输出控制。例如:磁盘,光盘以及usb等等。
      2)cpu   这个子系统使用调度程序为cgroup任务提供cpu的访问。
      3)cpuacct 产生cgroup任务的cpu资源报告。
      4)cpuset  如果是多核心的cpu,这个子系统会为cgroup任务分配单独的cpu和内存。
      5)devices  允许或拒绝cgroup任务对设备的访问。
      6)freezer   暂停和恢复cgroup任务。
      7)memory 设置每个cgroup的内存限制以及产生内存资源报告。
      8)net_cls   标记每个网络包以供cgroup方便使用。
      9)ns  名称空间子系统

cgroup是一个树状结构分布,系统启动的时候会创建一个cgroup系统,在start_kernel函数中,调用cgroup_init。cgroup_init比较简单,就是用kobject_create_and_add创建kobject,用register_filesystem注册了cgroup文件系统,用proc_create在proc下创建了cgroup开关。具体代码流程后面再看,先看框架内容。

就像它目录呈现的那样,可以这么理解Cgroup, 下面以ubuntu为例,3.13.0-24-generic内核代码,

首先有一个全局的root目录,是总控制开关:

1
2
oenhan@oenhan/sys/fs/cgroup$ ls
blkio  cpu  cpuacct  cpuset  devices  freezer  hugetlb  memory  perf_event  systemd

下面的目录就是各个子系统的控制开关,各个子系统如目录名所示,控制CPU的,内存的,等。事实上各个子系统的可以重叠到一个目录上控制的,如下:

1
mount-t cgroup -o cpu,cpuset,memory cpu_mem /cgroup/key

上面就是把cpu,cpuset,memory的3个子系统开关放到一个目录下控制,但个人看子系统重叠容易导致系统复杂度提高,而且管理容易混乱。而Ubuntu系统默认的管理方式,则简单明了,虽然可能不满足一个进程组直接控制的理想方案。

以cpu子系统为例看一下:

1
2
3
4
5
6
oenhan@oenhan/sys/fs/cgroup/cpu$ ls
cgroup.clone_children  cgroup.procs          cpu.cfs_period_us  cpu.shares  machine            release_agent  user
cgroup.event_control   cgroup.sane_behavior  cpu.cfs_quota_us   cpu.stat    notify_on_release  tasks
 
oenhan@oenhan/sys/fs/cgroup/cpu$ cattasks | wc-l
252

cpu下的很多控制文件后面细说,可以看一下tasks文件,里面即是系统当前的进程号,因为cpu是子系统的root,而OS中肯定有一个root_cgroup,所以tasks下是系统所有的进程。这里面的所有进程都会受当前目录下的控制文件限制,即CPU的使用率限制。这些tasks组成了一个进程组
但是进程组中的每个进程也会被区别对待的,这个就用到了树状结构的处理,

1
2
3
4
5
6
oenhan@oenhan/sys/fs/cgroup/cpu/$ mkdircpu_son
oenhan@oenhan/sys/fs/cgroup/cpu/cpu_son$ ls
cgroup.clone_children  cgroup.procs       cpu.cfs_quota_us  cpu.stat           tasks
cgroup.event_control   cpu.cfs_period_us  cpu.shares        notify_on_release
oenhan@oenhan/sys/fs/cgroup/cpu/cpu_son$ cattasks  | wc-l
0

进程组的进一步分化也是通过mkdir创建新的子系统控制目录进行的,cpu_son下tasks即是新的子进程组,需要你手工往里面添加进程。如果再分化再添加子目录,构成了一个树状结构,所有子目录下的子系统属性都是继承了当前root目录的系统属性,即CPU目录下创建的cpu_son默认也只是CPU子系统控制目录,而不带memory控制功能。
各个子系统的操作和cpu系统类似。



用 cgroups 管理 cpu 资源


这回说说怎样通过 cgroups 来管理 cpu 资源。先说控制进程的 cpu 使用。在一个机器上运行多个可能消耗大量资源的程序时,我们不希望出现某个程序占据了所有的资源,导致其他程序无法正常运行,或者造成系统假死无法维护。这时候用 cgroups 就可以很好地控制进程的资源占用。这里单说 cpu 资源。


cgroups 里,可以用 cpu.cfs_period_us 和 cpu.cfs_quota_us 来限制该组中的所有进程在单位时间里可以使用的 cpu 时间。这里的 cfs 是完全公平调度器的缩写。cpu.cfs_period_us 就是时间周期,默认为 100000,即百毫秒。cpu.cfs_quota_us 就是在这期间内可使用的 cpu 时间,默认 -1,即无限制。


跑一个耗 cpu 的程序


# echo 'while True: pass'|python &
[1] 1532
top 一下可以看到,这进程占了 100% 的 cpu


  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 1532 root      20   0  112m 3684 1708 R 99.6  0.7   0:30.42 python
...
然后就来对这个进程做一下限制。先把 /foo 这个控制组的限制修改一下,然后把进程加入进去。


echo 50000 >/sys/fs/cgroup/cpu/foo/cpu.cfs_quota_us
echo 1532 >/sys/fs/group/cpu/foo/tasks
可见,修改设置只需要写入相应文件,将进程加入 cgroup 也只需将 pid 写入到其中的 tasks 文件即可。这里将 cpu.cfs_quota_us 设为 50000,相对于 cpu.cfs_period_us 的 100000 即 50%。再 top 一下看看效果。


  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 1532 root      20   0  112m 3684 1708 R 50.2  0.7   5:00.31 python
...
可以看到,进程的 cpu 占用已经被成功地限制到了 50% 。这里,测试的虚拟机只有一个核心。在多核情况下,看到的值会不一样。另外,cfs_quota_us 也是可以大于 cfs_period_us 的,这主要是对于多核情况。有 n 个核时,一个控制组中的进程自然最多就能用到 n 倍的 cpu 时间。


这两个值在 cgroups 层次中是有限制的,下层的资源不能超过上层。具体的说,就是下层的 cpu.cfs_period_us 值不能小于上层的值,cpu.cfs_quota_us 值不能大于上层的值。


另外的一组 cpu.rt_period_us、cpu.rt_runtime_us 对应的是实时进程的限制,平时可能不会有机会用到。


在 cpu 子系统中,cpu.stat 就是用前面那种方法做的资源限制的统计了。nr_periods、nr_throttled 就是总共经过的周期,和其中受限制的周期。throttled_time 就是总共被控制组掐掉的 cpu 使用时间。


还有个 cpu.shares, 它也是用来限制 cpu 使用的。但是与 cpu.cfs_quota_us、cpu.cfs_period_us 有挺大区别。cpu.shares 不是限制进程能使用的绝对的 cpu 时间,而是控制各个组之间的配额。比如


/cpu/cpu.shares : 1024
/cpu/foo/cpu.shares : 2048
那么当两个组中的进程都满负荷运行时,/foo 中的进程所能占用的 cpu 就是 / 中的进程的两倍。如果再建一个 /foo/bar 的 cpu.shares 也是 1024,且也有满负荷运行的进程,那 /、/foo、/foo/bar 的 cpu 占用比就是 1:2:1 。前面说的是各自都跑满的情况。如果其他控制组中的进程闲着,那某一个组的进程完全可以用满全部 cpu。可见通常情况下,这种方式在保证公平的情况下能更充分利用资源。


此外,还可以限定进程可以使用哪些 cpu 核心。cpuset 子系统就是处理进程可以使用的 cpu 核心和内存节点,以及其他一些相关配置。这部分的很多配置都和 NUMA 有关。其中 cpuset.cpus、cpuset.mems 就是用来限制进程可以使用的 cpu 核心和内存节点的。这两个参数中 cpu 核心、内存节点都用 id 表示,之间用 “,” 分隔。比如 0,1,2 。也可以用 “-” 表示范围,如 0-3 。两者可以结合起来用。如“0-2,6,7”。在添加进程前,cpuset.cpus、cpuset.mems 必须同时设置,而且必须是兼容的,否则会出错。例如


# echo 0 >/sys/fs/cgroup/cpuset/foo/cpuset.cpus
# echo 0 >/sys/fs/cgroup/cpuset/foo/cpuset.mems
这样, /foo 中的进程只能使用 cpu0 和内存节点0。用


# cat /proc/<pid>/status|grep '_allowed_list'
可以验证效果。


cgroups 除了用来限制资源使用外,还有资源统计的功能。做云计算的计费就可以用到它。有一个 cpuacct 子系统专门用来做 cpu 资源统计。cpuacct.stat 统计了该控制组中进程用户态和内核态的 cpu 使用量,单位是 USER_HZ,也就是 jiffies、cpu 滴答数。每秒的滴答数可以用 getconf CLK_TCK 来获取,通常是 100。将看到的值除以这个值就可以换算成秒。


cpuacct.usage 和 cpuacct.usage_percpu 是该控制组中进程消耗的 cpu 时间,单位是纳秒。后者是分 cpu 统计的。


发现在 SLES 11 sp2、sp3 ,对应内核版本 3.0.13、 3.0.76 中,对 cpu 子系统,将 pid 写入 cgroup.procs 不会实际生效,要写入 tasks 才行。在其他环境中,更高版本或更低版本内核上均未发现。


转载自:http://xiezhenye.com/2013/10/%E7%94%A8-cgroups-%E7%AE%A1%E7%90%86-cpu-%E8%B5%84%E6%BA%90.html 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值