Linux上的Docker Engine也依赖于另一种称为CGroup的技术。CGroups 最初叫Process Container,顾名思义就是将进程放到一个组里进行统一控制。后来改名叫Control Groups。CGroups 是 Control Groups 的缩写,它为资源管理提供了一个统一框架,可以把系统任务及其子任务整合到按资源等级划分的不同任务组内。并且对这些任务组实施不同的资源分配方案。CGroups可以限制、记录、隔离进程组所使用的物理资源(包括CPU、memory、I/O等)。
cgroups有如下四个有趣的特点:
- cgroups的API以一个伪文件系统的方式实现,即用户可以通过文件操作实现cgroups的组织管理。
- cgroups的组织管理操作单元可以细粒度到线程级别,用户态代码也可以针对系统分配的资源创建和销毁cgroups,从而实现资源再分配和管理。
- 所有资源管理的功能都以“subsystem(子系统)”的方式实现,接口统一。
- 子进程创建之初与其父进程处于同一个cgroups的控制组。
相关概念
- 任务(task)。在 cgroups 中,任务就是系统的一个进程;
- 控制族群(control group)。控制族群就是一组按照某种标准划分的进程。Cgroups 中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用 cgroups 以控制族群为单位分配的资源,同时受到 cgroups 以控制族群为单位设定的限制;
- 层级(hierarchy)。控制族群可以组织成 hierarchical 的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性;
- 子系统(subsystem)。一个子系统就是一个资源控制器,比如 cpu 子系统就是控制 cpu 时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。
子系统
cgroups提供了subsystem(resource controllers)作为资源的控制器
- cpu (since Linux 2.6.24; CONFIG_CGROUP_SCHED)
用来限制cgroup的CPU使用率。 - cpuacct (since Linux 2.6.24; CONFIG_CGROUP_CPUACCT)
统计cgroup的CPU的使用率。 - cpuset (since Linux 2.6.24; CONFIG_CPUSETS)
绑定cgroup到指定CPUs和NUMA节点。 - memory (since Linux 2.6.25; CONFIG_MEMCG)
统计和限制cgroup的内存的使用率,包括process memory, kernel memory, 和swap。 - devices (since Linux 2.6.26; CONFIG_CGROUP_DEVICE)
限制cgroup创建(mknod)和访问设备的权限。 - freezer (since Linux 2.6.28; CONFIG_CGROUP_FREEZER)
suspend和restore一个cgroup中的所有进程。 - net_cls (since Linux 2.6.29; CONFIG_CGROUP_NET_CLASSID)
将一个cgroup中进程创建的所有网络包加上一个classid标记,用于tc和iptables。 只对发出去的网络包生效,对收到的网络包不起作用。 - blkio (since Linux 2.6.33; CONFIG_BLK_CGROUP)
限制cgroup访问块设备的IO速度。 - perf_event (since Linux 2.6.39; CONFIG_CGROUP_PERF)
对cgroup进行性能监控 - net_prio (since Linux 3.3; CONFIG_CGROUP_NET_PRIO)
针对每个网络接口设置cgroup的访问优先级。 - hugetlb (since Linux 3.5; CONFIG_CGROUP_HUGETLB)
限制cgroup的huge pages的使用量。 - pids (since Linux 4.3; CONFIG_CGROUP_PIDS)
限制一个cgroup及其子孙cgroup中的总进程数。
概念相互关系及基本规则
传统的Unix进程管理,实际上是先启动init进程作为根节点,再由init节点创建子进程作为子节点,而每个子节点由可以创建新的子节点,如此往复,形成一个树状结构。
Cgroup 与进程在以下方面类似:
- 它们是分级的,且
- 子 cgroup 会继承父 cgroup 的某些属性。
根本的不同是在某个系统中可同时存在不同的分级 cgroup。如果 Linux 进程模式是进程的单一树模式,那么 cgroup 模式是一个或者更多任务的独立、未连接树(例如:进程)。
需要多个独立 cgroup 分级,因为每个分级都会附加到一个或者多个子系统中。子系统代表单一资源,比如 CPU 时间或者内存。
基于虚拟文件系统的CGroup管理
Linux 使用了多种数据结构在内核中实现了 cgroups 的配置,关联了进程和 cgroups 节点,那么 Linux 又是如何让用户态的进程使用到 cgroups 的功能呢? Linux内核有一个很强大的模块叫 VFS (Virtual File System)。 VFS 能够把具体文件系统的细节隐藏起来,给用户态进程提供一个统一的文件系统 API 接口。 cgroups 也是通过 VFS 把功能暴露给用户态的,cgroups 与 VFS 之间的衔接部分称之为 cgroups 虚拟文件系统。使用cgroup很简单,挂载这个文件系统就可以了。这时通过挂载选项指定使用哪个子系统。
默认情况都是挂载到/sys/fs/cgroup目录下,当然挂载到其它任何目录都没关系。
//挂载一颗和所有subsystem关联的cgroup树到/sys/fs/cgroup
mount -t cgroup <cgroup name> /sys/fs/cgroup
//挂载一颗和cpuset subsystem关联的cgroup树到/sys/fs/cgroup/cpuset
mkdir /sys/fs/cgroup/cpuset
mount -t cgroup -o cpuset <cgroup name> /sys/fs/cgroup/cpuset
//挂载一颗与cpu和cpuacct subsystem关联的cgroup树到/sys/fs/cgroup/cpu,cpuacct
mkdir /sys/fs/cgroup/cpu,cpuacct
mount -t cgroup -o cpu,cpuacct <cgroup name> /sys/fs/cgroup/cpu,cpuacct
//挂载一棵cgroup树,但不关联任何subsystem,下面就是systemd所用到的方式
mkdir /sys/fs/cgroup/systemd
mount -t cgroup -o none,name=systemd <cgroup name> /sys/fs/cgroup/systemd
规则 1: 多个subsystem可附着到同一个hierarchy
例如在ubuntu中,systemd就是将和cpu,cpuacct两个subsystem挂载到了/sys/fs/cgroup/cpu,cpuacct 这个hierarchy
# mount |grep cpu,cpuacct
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
规则2: 一个subsystem不能同时附加到多个hierarchy,除非那个hierarchy不包含其他subsystem。
//尝试将cpu subsystem重新关联cpu_cg hierarchy 并且将这棵树mount到./cpu目录
# mkdir -p /mnt/cgroup/cpu_cg
# mount -t cgroup -o cpu cpu_cg /mnt/cgroup/cpu_cg
mount: cpu_cg is already mounted or /mnt/cgroup/cpu_cg busy
//但是将memory subsystem 关联到另一个Cgroup hierarchy是可以的
# mkdir -p /mnt/cgroup/mem_cg
# mount -t cgroup -o memory mem_cg /mnt/cgroup/mem_cg
# mount|grep memory
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
mem_cg on /mnt/cgroup/mem_cg type cgroup (rw,relatime,memory)
//实际上,用同样的参数挂载时,会重用现有的cgroup hierarchy,也即两个挂载点看到的内容是一样的
# mkdir -p /mnt/cgroup/cpu,cpuacct && cd /mnt/cgroup
# mount -t cgroup -o cpu,cpuacct new-cpu-cpuacct ./cpu,cpuacct
# mount |grep cpu,cpuacct
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
new-cpu-cpuacct on /mnt/cgroup/cpu,cpuacct type cgroup (rw,relatime,cpu,cpuacct)
# mkdir -p /sys/fs/cgroup/cpu,cpuacct/test; ls -l /mnt/cgroup/cpu,cpuacct|grep test
drwxr-xr-x 2 root root 0 1月 23 18:18 test
//clean
# umount new-cpu-cpuacct
# umount mem_cg
# cgdelete cpu,cpuacct:test
本质上将,一个subsystem只能关联到一颗cgroup hierarchy ,一旦关联并在这个hierarchy上创建了子cgroup,subsystems和这棵cgroup hierarchy就成了一个整体,不能再重新组合。
规则3:同一个task不能同时作为同一个hierarchy的不同cgroup的成员
每次在系统中创建新hierarchy时,该系统中的所有task都是那个hierarchy的默认 cgroup(我们称之为 root cgroup)的初始成员。
这样,单一task可以是在多个 cgroup 中。前提是这些cgroup不能同时位于同一个hierarchy。
首先我们尝试创建一个root cgroup, 在系统中可以创建任意多个不和任何subsystem关联的cgroup树,name是这棵树的唯一标记,当name指定的是一个新的名字时,将创建一颗新的cgroup树,但如果内核中已经存在一颗一样name的cgroup树,那么将mount已存在的这颗cgroup树
// 由于name=test的cgroup树在系统中不存在,所以这里会创建一颗新的name=test的cgroup树
# mkdir -p /mnt/cgroup/test && cd /mnt/cgroup
# mount -t cgroup -o none,name=test test ./test
// 系统为新创建的cgroup树的root cgroup生成了默认文件
# ls /mnt/cgroup/test
cgroup.clone_children cgroup.procs cgroup.sane_behavior notify_on_release release_agent tasks
// 新创建的cgroup树的root cgroup里包含系统中的所有进程
# wc -l ./test/cgroup.procs
188 ./test/cgroup.procs
// 创建子cgroup
# cd test && mkdir subcg
// 系统已经为新的子cgroup生成了默认文件
# ls subcg
cgroup.clone_children cgroup.procs notify_on_release tasks
//新创建的子cgroup中没有任何进程
# wc -l subcg/cgroup.procs
0 subcg/cgroup.procs
//重新挂载这棵树到test1,由于mount的时候指定的name=test,所以和上面挂载的是同一颗cgroup树,于是test1目录下的内容和test目录下的内容一样
# mkdir -p /mnt/cgroup/test1 && cd /mnt/cgroup
# mount -t cgroup -o none,name=test test ./test1
# ls ./test1
cgroup.clone_children cgroup.procs cgroup.sane_behavior notify_on_release release_agent subcg tasks
#清理
# umount /mnt/cgroup/test1
# umount /mnt/cgroup/test
# rm -rf /mnt/cgroup/test /mnt/cgroup/test1
再试一个例子:
// 换一个console 启动一个bash
$ ps -ef|grep $$
chic 1944 1939 0 09:56 pts/1 00:00:00 -bash
//查看这个bash所属的cgroup
$ cat /proc/1944/cgroup
11:blkio:/user.slice
10:freezer:/
9:memory:/user.slice
8:cpuset:/
7:devices:/user.slice
6:net_cls,net_prio:/
5:hugetlb:/
4:pids:/user.slice/user-1000.slice
3:perf_event:/
2:cpu,cpuacct:/user.slice
1:name=systemd:/user.slice/user-1000.slice/session-2.scope
每一行包含用冒号隔开的三列,他们的意思分别是
- cgroup树的ID, 和/proc/cgroups文件中的ID一一对应。
- 和cgroup树绑定的所有subsystem,多个subsystem之间用逗号隔开。这里name=systemd表示没有和任何subsystem绑定,只是给他起了个名字叫systemd。
- 进程在cgroup树中的路径,即进程所属的cgroup,这个路径是相对于挂载点的相对路径。
# mount -t cgroup -o none,name=cgroup cgroup /mnt/cgroup
# mkdir -p /mnt/cgroup/child1
# mkdir -p /mnt/cgroup/child2
//检查之前的bash现在也属于这个cgroup
/mnt/cgroup# cat cgroup.procs |grep 1944
1944
//将bash移入 child1 cgroup
# cd child1
# echo 1944 > cgroup.procs
# cat /proc/1944/cgroup
13:name=cgroup:/child1
11:blkio:/user.slice
10:freezer:/
9:memory:/user.slice
8:cpuset:/
7:devices:/user.slice
6:net_cls,net_prio:/
5:hugetlb:/
4:pids:/user.slice/user-1000.slice
3:perf_event:/
2:cpu,cpuacct:/user.slice
1:name=systemd:/user.slice/user-1000.slice/session-2.scope
//再试着将bash放入 child2 cgroup
# echo 1944 > /mnt/cgroup/child2/cgroup.procs
# cat /proc/1944/cgroup
13:name=cgroup:/child2
11:blkio:/user.slice
10:freezer:/
9:memory:/user.slice
8:cpuset:/
7:devices:/user.slice
6:net_cls,net_prio:/
5:hugetlb:/
4:pids:/user.slice/user-1000.slice
3:perf_event:/
2:cpu,cpuacct:/user.slice
1:name=systemd:/user.slice/user-1000.slice/session-2.scope
//可以看见bash现在属于child2 而从child1 移除了,一个task不会同时属于同一hierarchy的不同cgroup
规则4:子进程(task)总是自动成为父进程所在的cgroup的成员,然后可以移动到不同的cgroup中去。
接着之前的例子:
//在新的bash(1944)中在启动一个bash(12110)
$ pstree -p 1944
bash(1944)───bash(12110)───pstree(12142)
$ cat /proc/12110/cgroup
13:name=cgroup:/child2
11:blkio:/user.slice
10:freezer:/
9:memory:/user.slice
8:cpuset:/
7:devices:/user.slice
6:net_cls,net_prio:/
5:hugetlb:/
4:pids:/user.slice/user-1000.slice
3:perf_event:/
2:cpu,cpuacct:/user.slice
1:name=systemd:/user.slice/user-1000.slice/session-2.scope
使用libcgroup工具包
libcgroup工具安装
- redhat系统:
yum install libcgroup - ubuntu系统:
sudo apt-get install cgroup-bin cgroup-lite cgroup-tools cgroupfs-mount libcgroup1
cgroup服务的启动和停止:
- redhat系统(Sys V init):
service cgconfig start|stop - ubuntu系统(Systemd):
systemctl start cgmanager.service
systemctl start run-cgmanager-fs.mount
systemctl start cgroupfs-mount.service
CGroups功能测试(基于Ubuntu)
在Ubuntu上安装完成cgroup工具包后,系统会创建以下文件:
- /sys/fs/cgroup; 该文件夹包含一堆cgroup类型挂在的虚拟文件用来代表特定的cgroup层级.
- /etc/init/cgroup-lite.conf. cgroup-lite.conf creates the /sys/fs/cgroup directory, based on the contents of
- /proc/cgroups, 这个文件指定了cgroup-lite 应该创建哪些Control group.
安装cgroup-bin后,unbuntu的systemd已经帮我们将各个subsystem和cgroup树关联并挂载好了
# mount |grep cgroup
tmpfs on /sys/fs/cgroup type tmpfs (rw,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids,release_agent=/run/cgmanager/agents/cgm-release-agent.pids)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
# ls /sys/fs/cgroup/
blkio cgmanager cpu cpuacct cpu,cpuacct cpuset devices freezer hugetlb memory net_cls net_cls,net_prio net_prio perf_event pids systemd
cgroup-bin 包含的基本命令
- /usr/bin/cgclassify – cgclassify命令是用来将运行的任务移动到一个或者多个cgroup。
- /usr/bin/cgcreate – cgcreate在层级中创建新cgroup。
- /usr/bin/cgdelete – cgdelete命令删除指定的cgroup。
- /usr/bin/cgexec – cgexec命令在指定的cgroup中运行任务。
- /usr/bin/cgget – cgget命令显示cgroup参数。
- /usr/bin/cgset – cgset 命令为 cgroup 设定参数。
- /usr/bin/lscgroup – lscgroup 命令列出层级中的 cgroup。
- /usr/bin/lssubsys – lssubsys 命令列出包含指定子系统的层级
- /usr/sbin/cgclear – cgclear 命令是用来删除层级中的所有cgroup。
- /usr/sbin/cgconfigparser – cgconfigparser命令解析cgconfig.conf文件和并挂载层级。
- /usr/sbin/cgrulesengd – cgrulesengd 在 cgroup 中发布任务。
相关配置文件:
- cgred.conf – cgred.conf是cgred服务的配置文件。
- cgrules.conf – cgrules.conf 包含用来决定何时任务术语某些 cgroup的规则。
- cgconfig.conf – 在cgconfig.conf文件中定义cgroup。
cgconfig.conf
cgconfig.conf是libcgroup用于定义控制组及其参数和挂载点的配置文件。 该文件由mount,group和default部分组成。 这些部分可以是任意的顺序,所有这些部分都是可选的。 (refer to “man cgconfig.conf”)
- mount section
mount {
<controller> = <path>;
...
}
//controller : kernel subsystem 名称
//path : 与给controller关联的hierarichy的目录路径。
- group section
group <name> {
[permissions]
<controller> {
<param name> = <param value>;
...
}
...
}
//name: Name of the control group
//permissions: 可选项,指定cgroup对应的挂载点文件系统的权限,root用户拥有所有权限
//param name 和 param value:子系统的属性及其属性值
- default section
default {
perm {
task {
uid = <task user>;
gid = <task group>;
fperm = <file permissions>
}
admin {
uid = <admin name>;
gid = <admin group>;
dperm = <directory permissions>
fperm = <file permissions>
}
}
}
例如:
mount {
cpu = /sys/fs/cgroup/cpu;
cpuacct = /sys/fs/cgroup/cpu;
}
group daemons/www {
perm {
task {
uid = root;
gid = webmaster;
fperm = 770;
}
admin {
uid = root;
gid = root;
dperm = 775;
fperm = 744;
}
}
cpu {
cpu.shares = "1000";
}
}
group daemons/ftp {
perm {
task {
uid = root;
gid = ftpmaster;
fperm = 774;
}
admin {
uid = root;
gid = root;
dperm = 755;
fperm = 700;
}
}
cpu {
cpu.shares = "500";
}
}
相当于:
mkdir /sys/fs/cgroup/cpu
mount -t cgroup -o cpu,cpuacct cpu /sys/fs/cgroup/cpu
mkdir /sys/fs/cgroup/cpu/daemons
mkdir /sys/fs/cgroup/cpu/daemons/www
chown root:root /sys/fs/cgroup/cpu/daemons/www/*
chown root:webmaster /sys/fs/cgroup/cpu/daemons/www/tasks
echo 1000 > /sys/fs/cgroup/cpu/daemons/www/cpu.shares
mkdir /sys/fs/cgroup/cpu/daemons/ftp
chown root:root /sys/fs/cgroup/cpu/daemons/ftp/*
chown root:ftpmaster /sys/fs/cgroup/cpu/daemons/ftp/tasks
echo 500 > /sys/fs/cgroup/cpu/daemons/ftp/cpu.shares
cgroup manager
cgroup manager(cgmanager)提供了一个D-Bus服务,允许程序和用户管理cgroup,而不需要直接了解或访问cgroup文件系统。 对于来自与管理者相同名称空间任务的请求,管理者可以直接执行所需的安全检查,以确保请求是合法的。 对于其他请求(例如来自容器中任务的请求),必须进行增强的D-Bus请求,其中将进程,用户和组ID作为SCM_CREDENTIALS传递,以便内核将标识符映射到其全局主机值。
为了便利所有用户使用简单的D-Bus呼叫,一个“cgroup管理器代理”(cgproxy)在容器中自动启动。 代理服务器接受来自相同命名空间中任务的标准D-Bus请求,并将它们转换为SCM传递给cgmanager的D-Bus请求。
创建一个运行cpu密集编译的新cgroup的简单例子如下所示:
cgm create cpuset build1
cgm movepid cpuset build1 $$
cgm setvalue cpuset build1 cpuset.cpus 1
make
cgm
cgm是一个简化cgroup manager请求的客户端脚本。 它只是调用dbus-send向运行中的cgmanager或cgproxy发送请求。
下面的例子中,首先创建一个名为foo的cgroup然后将当前shell移入其中:
sudo cgm create all foo
sudo cgm chown all foo $(id -u) $(id -g)
cgm movepid all foo $$
//then to freeze the cgroup
cgm setvalue freezer foo freezer.state FROZEN
cgproxy
cgproxy是cgmanager的D-Bus代理。 由于非初始pid或user namespace中的进程将引用任务,用户ID或组ID 在初始名称空间中是错乱的。cgproxy将依据内核将它们作为SCM凭证发送给cgmanager 翻译标识符。 通过这种方式,容器中的程序和用户可以使用相同的简单D-Bus调用通过代理服务器进行cgroup管理请求,就像它们在基本主机上直接与cgmanager交互一样。
example
创建一个耗CPU的脚本:
$ cat cpu.sh
#!/bin/bash
tmp=1
while [ True ];do
tmp=$tmp
done;
运行一下:
#top -p `pidof -x cpu.sh`
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
26855 chic 20 0 14264 912 808 R 100.0 0.0 0:08.70 cpu.sh
创建一个cpu受限的cgroup
# cgcreate -a root -t chic -g cpu:mycpulimitgrp
# ls -l /sys/fs/cgroup/cpu/mycpulimitgrp/
total 0
-rw-r--r-- 1 root root 0 1月 24 17:32 cgroup.clone_children
-rw-r--r-- 1 root root 0 1月 24 17:32 cgroup.procs
-r--r--r-- 1 root root 0 1月 24 17:32 cpuacct.stat
-rw-r--r-- 1 root root 0 1月 24 17:32 cpuacct.usage
-r--r--r-- 1 root root 0 1月 24 17:32 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 1月 24 17:32 cpu.cfs_period_us
-rw-r--r-- 1 root root 0 1月 24 17:32 cpu.cfs_quota_us
-rw-r--r-- 1 root root 0 1月 24 17:32 cpu.shares
-r--r--r-- 1 root root 0 1月 24 17:32 cpu.stat
-rw-r--r-- 1 root root 0 1月 24 17:32 notify_on_release
-rw-r--r-- 1 chic root 0 1月 24 17:32 tasks
# cat /sys/fs/cgroup/cpu/mycpulimitgrp/cpu.cfs_period_us
100000
# cat /sys/fs/cgroup/cpu/mycpulimitgrp/cpu.cfs_quota_us
-1
//或者
# cgget -g cpu:mycpulimitgrp
mycpulimitgrp:
cpu.shares: 1024
cpu.cfs_quota_us: -1
cpu.stat: nr_periods 0
nr_throttled 0
throttled_time 0
cpu.cfs_period_us: 100000
- cpu.cfs_period_us 表示将每个cpu时间片分成100000份。
- cpu.cfs_period_us 表示当前这个组中的task(tasks中的taskid)将分配多少比例的cpu时间片。如果是双核cpu,这里就表示最多可以用到200%的CPU。
为我们的cgroup分配 50% cpu的配额
# echo 50000 > /sys/fs/cgroup/cpu/mycpulimitgrp/cpu.cfs_quota_us
//或者
# cgset -r cpu.cfs_quota_us=50000 mycpulimitgrp
$ cgexec -g cpu:mycpulimitgrp ./cpu.sh
$ top -p `pidof -x cpu.sh`
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
29224 chic 20 0 14264 904 800 R 50.0 0.0 0:21.42 cpu.sh
//stop cpu.sh and clean
# cgdelete -r cpu:mycpulimitgrp