欢迎转载,转载请参见文章末尾处要求
Cgroups介绍
linux内核提供了cgroups控制组(control groups)的功能,最初由google的工程师提出,后来被整合进Linux内核。Cgroups也是LXC(Linux Container容器是一种内核虚拟化技术,可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性)为实现虚拟化所使用的资源管理手段。
cgroups适用于多种应用场景,从单个进程的资源控制,到实现操作系统层次的虚拟化提供了以下功能:
1.限制进程组可以使用的资源数量(Resource limiting )。比如:memory子系统可以为进程组设定一个memory使用上限,一旦进程组使用的内存达到限额再申请内存,就会出发OOM(out of memory)。
2.进程组的优先级控制(Prioritization )。比如:可以使用cpu子系统为某个进程组分配特定cpu share。
3.记录进程组使用的资源数量(Accounting )。比如:可以使用cpuacct子系统记录某个进程组使用的cpu时间。
4.进程组隔离(Isolation)。比如:使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间。
5.进程组控制(Control)。比如:使用freezer子系统可以将进程组挂起和恢复。
Cgroups基本概念
首先需要理解几个cgroups的概念:
1.控制组(control group)。控制组就是一组按照某种标准划分的进程。Cgroups中的资源控制都是以控制组为单位实现。一个进程可以加入到某个控制组,也从一个进程组迁移到另一个控制组。一个进程组的进程可以使用cgroups以控制组为单位分配的资源,同时受到cgroups以控制组为单位设定的限制。
2.层级(hierarchy)。控制组可以组织成hierarchical的形式,既一颗控制组树。控制组树上的子节点控制组是父节点控制组的孩子,继承父控制组的特定的属性。
3.子系统(subsytem)。一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。
实例说明:
1.创建一个层级,挂载三个子系统
mount -t cgroup -o cpu,cpuset,memory cpu_and_mem /cgroup/cpu_and_mem
这个命令就创建一个名为cpu_and_mem的层级,这个层级上附加了cpu,cpuset,memory三个子系统,并把层级挂载到了/cgroup/cpu_and_mem.
2.创建一个控制组:
cd /cgroup/cpu_and_mem
mkdir foo
通过以上两个命令,我们就在刚才创建的层级下创建了一个叫foo的cgroup。
三者之间的相互关系:
1.每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup ,此cgroup在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员。
# mkdir /cgroup
# mount -t cgroup cgroup -o cpu/cgroup
# mkdir /cgroup2
# mount -t cgroup cgroup -o cpu/cgroup2
# cd cgroup2
# mkdir cgroup4
# cd ../../ cgroup
# mkdir cgroup1
# ls
cgroup.procs cpu.rt_period_us notify_on_release
cgroup1 cpu.rt_runtime_us release_agent
cgroup4 cpu.shares tasks
通过这个例子可见本来想将一个子系统cpu附加到cgroup和cgroup2两个层级上,却发现操作cgroup和cgroup2的效果完全一样,可见不应也没有必要将一个子系统附加到多个层级上。
3.一个层级可以附加多个子系统
# mount -t cgroup cgroup -ofreezer,cpuset /cg
# ls /cg/
cgroup.procs cpuset.memory_spread_page
cpuset.cpu_exclusive cpuset.memory_spread_slab
cpuset.cpus cpuset.mems
cpuset.mem_exclusive cpuset.sched_load_balance
cpuset.mem_hardwall cpuset.sched_relax_domain_level
cpuset.memory_migrate notify_on_release
cpuset.memory_pressure release_agent
cpuset.memory_pressure_enabled tasks
如果控制组的成员共享子系统属性,采用此方式控制操作相对简单,否则建议一个层级附加一个子系统。
4.一个任务可以是多个cgroup的成员,但是这些cgroup必须在不同的层级。
5.系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的cgroup。如果你把某一个层次结构中的任务添加到了同层的另外一个cgroup中,那么该任务会被其之前所在的cgroup清除。
子系统说明与实例
一个子系统就是一个资源控制器,比如cpu子系统就是控制cpu时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。管理员需要对系统中运行的任务进行分组,然后为每个组分配资源,比如CPU时间,系统内存或者资源的组合,管理员可以控制系统资源的分配、优先顺序、拒绝、管理和监控,可以更好的根据用户和资源的情况合理的分配资源,提高系统总体效率。内核为了便于管理资源使用子系统表示单一资源,比如CPU时间,系统内存等,然后每个层级都会关联一个或者多个子系统,进而实现资源管理和配置。当前的内核支持如下可用的子系统:
cpu:对系统中CPU资源进行管理。
cpuset:该系统为控制组分配独立CPU和内存节点。
cpuacct:该子系统自动生成控制组使用CPU资源的报告。
memory:提供控制组对内存使用的限制以及自动生成这些任务内存资源使用报告。
devices:提供设备访问白名单机制。
freezer:该系统提供挂起或者恢复控制组的机制。
ns:名称空间子系统。
后面将分别介绍这些子系统。
Cpu子系统
# mount -t cgroup none /mnt/cgroup/-o cpu
# ls /mnt/cgroup/
cgroup.procs notify_on_release tasks
cpu.shares rt_runtime_us rt_period_us
cpu 子系统调度对 cgroup 的 CPU 访问。可根据以下参数调度对 CPU 资源的访问:
cpu.shares
包含用来指定在cgroup 中的任务可用的相对共享 CPU 时间的整数值。例如:在两个 cgroup 中都将 cpu.shares 设定为 1 的任务将有相同的 CPU 时间,但在 cgroup 中将 cpu.shares 设定为 2的任务可使用的 CPU 时间是在 cgroup 中将 cpu.shares 设定为 1 的任务可使用的 CPU 时间的两倍。
启动后,挂载一个只划分CPU资源的cgroup,并创建childgroup1和childgroup2两个子组:
# mkdir/dev/cgroup/cpu
# mount-t cgroup cgroup -o cpu /dev/cgroup/cpu
# mkdirchildgroup1
# mkdirchildgroup2
# ls
cgroup.procs cpu.rt_period_us notify_on_release
childgroup1 cpu.rt_runtime_us release_agent
childgroup2 cpu.shares tasks
启动3个非实时死循环任务:
PID PPID USER STAT VSZ %MEM CPU %CPU COMMAND
58 27 root R 800 0.6 0 29.9 ./test3
56 27 root R 800 0.6 0 26.6 ./test1
57 27 root R 800 0.6 0 26.6 ./test2
观察Top统计,各任务均匀占用cpu
随后将test3关联至group2,test1、test2关联至group1
# echo 56> /dev/cgroup/cpu/childgroup1/tasks
# echo 57> /dev/cgroup/cpu/childgroup1/tasks
# echo 58> /dev/cgroup/cpu/childgroup2/tasks
观察发现,相同优先级的任务test3的cpu占用率是两外两个任务之和:
58 27 root R 800 0.6 0 45.0 ./test3
56 27 root R 800 0.6 0 22.5 ./test1
57 27 root R 800 0.6 0 22.5 ./test2
将childgroup1组的shares值设置为childgroup2的两倍,
echo 2048 >/dev/cgroup/cpu/childgroup1/cpu.shares
后观察相同优先级的test1和test2任务运行时间和是test3的两倍。
58 27 root R 800 0.6 0 28.5 ./test3
57 27 root R 800 0.6 0 28.5 ./test2
56 27 root R 800 0.6 0 25.7 ./test1
注:cgroup.procs操作的是tgid(线程组)集合。
cpu.rt_runtime_us
以微秒(μs,这里以“us”代表)为单位指定在某个时间段中 cgroup 中的任务对 CPU 资源的最长连续访问时间。建立这个限制是为了防止一个 cgroup 中的任务独占 CPU 时间。如果 cgroup中的任务应该可以每 5 秒中可有 4 秒时间访问 CPU 资源,请将 cpu.rt_runtime_us 设定为4000000,并将 cpu.rt_period_us 设定为 5000000。
cpu.rt_period_us
以微秒(μs,这里以“us”代表)为单位指定在某个时间段中 cgroup 对 CPU 资源访问重新分配的频率。
写一个实时任务死循环:
#include <sched.h>
int main()
{
int prio = 50;
sched_setscheduler(0, SCHED_RR,(struct sched_param*)&prio);
while(1);
return 0;
}
运行
PID PPID USER STAT VSZ %MEM CPU %CPU COMMAND
63 27 root R 800 0.6 0 50 ./rt
64 27 root R 800 0.6 0 50 ./rt1
将任务关联至group1
# echo 63 >/dev/cgroup/cpu/childgroup1/tasks
# echo 64 >/dev/cgroup/cpu/childgroup2/tasks
默认cpu. rt_runtime_us为-1,小于0表示运行不受限。
随后echo 300000 > /dev/cgroup/cpu/childgroup1/cpu. rt_runtime_us,单位是us
echo 400000 >/dev/cgroup/cpu/childgroup2/cpu. rt_runtime_us
此时观察实时rt任务运行每秒钟执行300ms,rt1任务运行每秒钟执行400ms。
对于一个组的各个child group的运行cpu占用率之和不能超过parent group的cpu占用率。否则设置失败:
# echo 900000 > cpu.rt_runtime_us(900000+400000>1000000)
# cat cpu.rt_runtime_us
300000
其中组调度有几个相关的选择编译选项,分别如下:
CONFIG_GROUP_SCHED:组调度生效选项,要选择此项才能开启CFS组调度功能
CONFIG_RT_GROUP_SCHED:支持实时进程组调度(也就是SCHED_FIFO和SCHED_RR)
CONFIG_FAIR_GROUP_SCHED:支持CFS进程组调度(也就是SCHED_NORMAL和SCHED_BATCH)
Cpuset子系统
# mount -t cgroup none/mnt/cgroup/ -o cpuset
# ls -l /mnt/cgroup/
cgroup.procs cpuset.memory_spread_page
cpuset.cpu_exclusive cpuset.memory_spread_slab
cpuset.cpus cpuset.mems
cpuset.mem_exclusive cpuset.sched_load_balance
cpuset.mem_hardwall cpuset.sched_relax_domain_level
cpuset.memory_migrate notify_on_release
cpuset.memory_pressure release_agent
cpuset.memory_pressure_enabled tasks
cpuset 子系统为 cgroup 分配独立 CPU 和内存节点。可根据用以下参数指定每个 cpuset:
cpuset.cpus
指定允许这个 cgroup中任务访问的 CPU。这是一个用逗号分开的列表,格式为 ASCII,使用小横线("-")代表范围。例如:0-2,16代表 CPU 0、1、2 和 16。
# mount -t cgroup -o cpuset none/cgroup/cpu
# mkdir cputest
# echo 0 > cpusettest /cpuset.cpus
启动一个进程pid19425,查看其cpu affinity
# taskset -p 19425
pid 19425's current affinity mask: 3
# taskset -c -p 19425
pid 19425's current affinity list: 0,1
# echo 19425 > cpusettest /tasks
# taskset -c -p 19425
pid 19425's current affinity list: 0
可以看出由于cpusettest只有CPU 0,当把进程挂到cgroup cpusettest上之后,其CPU affinity也变为0
更进一步在cpusettest创建子控制组:
# mkdir cpusettest1
# cd cpusettest1/
# cat cpuset.cpus
# echo 1 > cpuset.cpus
# cat cpuset.cpus
# echo 0 > cpuset.cpus
# cat cpuset.cpus
0
可见子控制组的cpuset.cpus范围必须控制在父控制组的范围内进行设定,否则不能成功,这个也是控制组层级管理的精髓。
注意,根控制组的cpuset.cpus是只读的,不可以修改
# echo 0-3 > cpuset.cpus
# cat cpuset.cpus
0-31
cpuset.mems
指定允许这个 cgroup中任务可访问的内存节点。这是一个用逗号分开的列表,格式为 ASCII,使用小横线("-")代表范围。
# mount -t cgroup none /mnt/cgroup/-o cpuset
# cat /mnt/cgroup/mems
cat: can't open '/mnt/cgroup/mems':No such file or directory
# cat /mnt/cgroup/cpuset.mems
0
#mkdir memset
# cd memset
#mkdir /mnt/cgroup/memstest
# cat cpuset.mems
# echo 0 > cpuset.mems
# cat cpuset.mems
0
#
查看cpuset.mems,这里显示的是0,说明默认情况下我们的系统可以使用结点0的内存系统,大多数系统是单路控制,不是NUMA,所以只看到了0。
另外需要强调新创建的CPUSET的mems和cpus都是空的,使用前必须先初始化,否则不能使用cpuset功能。
顶层CPUSET包含了系统中的所有cpuset.mems,而且是只读的,不能更改。
cpuset.memory_migrate
当一个任务从一个CPUSET1(mems值为0)迁移至另一个CPUSET2(mems值为1)的时候,此任务在节点0上分配的页面内容将迁移至节点1上分配新的页面(将数据同步到新页面),这样就避免了此任务的非本地节点的内存访问。
默认情况下禁止内存迁移(0)且页就保留在原来分配的节点中,即使在 cpuset.mems 中现已不再指定这个节点。如果启用(1),则该系统会将页迁移到由 cpuset.mems 指定的新参数中的内存节点中。
cpuset.cpu_exclusive
包含指定是否其它cpuset 及其平级组可共享为这个 cpuset 指定的 CPU 的标签(0 或者1)。默认情况下(0)CPU 不是专门分配给某个 cpuset 的。
这样限定后,此CPUSET中所有的任务都将使用限定的CPU。
在上面试验的基础上下面把cgrouptinker的cpu_exclusive设为1。
# echo 1 > cpusettest /cpuset.cpu_exclusive
# mkdir tailor
# echo 0 > cpusettest2/cpuset.cpus
#cat cpusettest2/cpuset.cpus
此时为空,设置没有成功!
# echo 1 >tailor/cpuset.cpus
#cat cpusettest2/cpuset.cpus
1
设置成功!
cpuset.mem_exclusive
与cpuset.cpu_exclusive类似,cpuset.mem_exclusive包含指定是否其它cpuset 可共享为这个 cpuset 指定的内存节点的标签(0 或者 1)。默认情况下(0)内存节点不是专门分配给某个 cpuset 的。专门为某个 cpuset 保留内存节点(1)。
cpuset.mem_hardwall
即便使用了cpuset.mems对内存进行限制,但系统中的任务并不能完全孤立,比如还是可能会全局共享Page Cache,动态库等资源,因此内核在某些情况下还是可以允许打破这个限制,如果不允许内核打破这个限制,需要设定CPUSET的内存硬墙标志即mem_hardwall置1即可,启用 hardwall 时每个任务的用户分配应保持独立,CPUSET默认是软墙。
硬软墙用于Buddy系统的页面分配,优先级高于内存策略,请参考内核函数:
cpuset_zone_allowed_hardwall()和cpuset_zone_allowed_softwall()。
cpuset.memory_pressure_enabled
是否计算cpuset中内存压力。内存压力是指当前系统的空闲内存不能满足当前的内存分配请求的速率。计算出的值会输出到 cpuset.memory_pressure,报告为尝试每秒再生内存的整数值再乘1000。
cpuset.memory_pressure
包含运行在这个cpuset 中产生的平均内存压力的只读文件。启用cpuset.memory_pressure_enabled 时,这个伪文件中的值会自动更新,否则值为0。
cpuset.memory_spread_slab
包含指定是否应在cpuset 的各内存节点上平均分配用于文件输入/输出操作的内核缓存的标签(0 或者 1)。默认情况是 0,即不尝试平均分配内核缓存,并将缓存放在生成这些缓存的进程所运行的同一节点中。
#ifdef CONFIG_NUMA
struct page *__page_cache_alloc(gfp_t gfp)
{
if (cpuset_do_page_mem_spread()) {
int n = cpuset_mem_spread_node();
return alloc_pages_exact_node(n, gfp, 0);
}
return alloc_pages(gfp, 0);
}
cpuset.memory_spread_page
包含指定是否应将文件系统缓冲平均分配给这个 cpuset 的内存节点的标签(0 或者 1)。默认情况为 0,不尝试为这些缓冲平均分配内存页面,且将缓冲放置在运行生成缓冲的进程的同一节点中。
cpuset.sched_load_balance
包含指定是否在这个cpuset 中跨 CPU 平衡负载。默认是 1,即内核将超载 CPU 中的进程移动到负载较低的 CPU 中以便平衡负载,echo 0 > sched_load_balance (禁止负载均衡)。
这个设置对smp多核差异化运行有很重要的意义。
需要注意:如果在任意上级cgroup 中启用负载平衡,则在 cgroup 中设定这个sched_load_balance没有任何效果,因为已经在较高一级 cgroup 中处理了负载平衡。因此,要在 cgroup 中禁用负载平衡,还要在该层级的每一个上级 cgroup 中禁用负载平衡。但是上层禁用了调度均衡,在子组内部是可以设置调度均衡的。
cpuset.sched_relax_domain_level
包含 -1 到小正数间的整数,它代表内核应尝试平衡负载的 CPU 宽度范围。如果cpuset.sched_load_balance设为0,则sched_relax_domain_level值毫无意义。根据不同系统构架这个值的具体效果不同:
sched_relax_domain_level的数值代表允许均衡的调度域级别,大于此级别的调度域层次将禁用均衡,而其余级别的调度域都开启:
-1 : 无效果,系统默认均衡方式
0 - 设置此值会禁用所有调度域的均衡
1 - 超线程域
2 - 核域
3 - 物理域
4 - NUMA域
5 - ALLNODES模式的NUMA域
Cpuacct子系统
# mount -t cgroup none /mnt/cgroup/-o cpuacct
# ls -l /mnt/cgroup/
-r--r--r-- 1 root root 0 Jan 1 00:05 cgroup.procs
-r--r--r-- 1 root root 0 Jan 1 00:05 cpuacct.stat
-rw-r--r-- 1 root root 0 Jan 1 00:05 cpuacct.usage
-r--r--r-- 1 root root 0 Jan 1 00:05 cpuacct.usage_percpu
-rw-r--r-- 1 root root 0 Jan 1 00:05 notify_on_release
-rw-r--r-- 1 root root 0 Jan 1 00:05 release_agent
-rw-r--r-- 1 root root 0 Jan 1 00:05 tasks
CPU Accounting(cpuacct)子系统自动生成 cgroup 中任务所使用的 CPU 资源报告,其中包括子组群中的任务。三个可用报告为:
cpuacct.stat
报告这个 cgroup 及其子组群中的任务使用用户模式和系统(内核)模式消耗的 CPU 循环数(单位由系统中的 USER_HZ 定义)。
# cat cpuacct.stat
user 7488
system 19763
cpuacct.usage
报告这个 cgroup 中所有任务(包括层级中的低端任务)消耗的总 CPU 时间(纳秒)。
# cat cpuacct.usage
268224611120
cpuacct.usage_percpu
报告这个 cgroup 中所有任务(包括层级中的低端任务)在每个 CPU 中消耗的 CPU 时间(以纳秒为单位)。
# cat cpuacct.usage_percpu
40976674832 29264037792 3963612296035058075616 33668163664 33219285808 28966073136 29855472128
Memory子系统
# mount -t cgroup none /mnt/cgroup/-o memory
cgroup.procs memory.soft_limit_in_bytes
memory.failcnt memory.stat
memory.force_empty memory.swappiness
memory.limit_in_bytes memory.usage_in_bytes
memory.max_usage_in_bytes memory.use_hierarchy
memory.memsw.failcnt notify_on_release
memory.memsw.limit_in_bytes release_agent
memory.memsw.max_usage_in_bytes tasks
memory.memsw.usage_in_bytes
memory 子系统自动生成 cgroup 中任务使用的内存资源报告,并设定由那些任务使用的内存限制:
memory.stat
报告大范围内存统计,如下表所述:
统计 | 描述 |
cache | 页缓存,包括 tmpfs(shmem),单位为字节 |
Rss | 匿名和 swap 缓存,不包括 tmpfs(shmem),单位为字节 |
Mapped_file | memory-mapped 映射的文件大小,包括 tmpfs(shmem),单位为字节 |
pgpgin | 存入内存中的页数 |
pgpgout | 从内存中读出的页数 |
swap | swap 用量,单位为字节 |
Active_anon | 在活跃的最近最少使用(least-recently-used,LRU)列表中的匿名和 swap 缓存,包括 tmpfs(shmem),单位为字节 |
Inactive_anon | 不活跃的 LRU 列表中的匿名和 swap 缓存,包括tmpfs(shmem),单位为字节
|
Active_file | 活跃 LRU 列表中的 file-backed 内存,以字节为单位 |
Inactive_file | 不活跃 LRU 列表中的 file-backed 内存,以字节为单位 |
unevictable | 无法再生的内存,以字节为单位 |
hierarchical_memory_limit | 包含 memory cgroup 的层级的内存限制,单位为字节 |
hierarchical_memsw_limit | 包含 memory cgroup 的层级的内存加 swap 限制,单位为字节 |
memory.limit_in_bytes
设定用户内存的最大量(包括文件缓存)。如果没有指定单位,则将该数值理解为字节。但是可以
使用前缀代表更大的单位 -k或者 K 代表千字节,m 或者 M 代表 MB,g 或者 G 代表GB。
在memory.limit_in_bytes 中写入 -1 删除现有限制。
注意不能使用memory.limit_in_bytes 限制 root cgroup;您只能在该层级中较低的组群中应用这些值。
limit_in_bytes限制内存的范围包括:
1.page fault发生时,有两种情况内核需要给进程分配新的页框。一种是进程请求调页,另一种是copy on write。
2.内核在handle_pte_fault中进行处理时,还有一种情况是pte存在且页又没有映射文件。这种情况说明页面之前在内存中,但是后面被换出到swap空间了。
3.当内核将page加入到page cache中时, mem_cgroup_cache_charge函数正是处理这种情况,它被植入到系统处理page cache的add_to_page_cache_locked函数中。
4.最后mem_cgroup_prepare_migration是用于处理内存迁移中的范围限制。
memory.memsw.limit_in_bytes
设定最大内存与 swap 用量之和。如果没有指定单位,则将该值解读为字节。但是可以使用前缀代表更大的单位 - k 或者 K 代表千字节,m 或者 M 代表 MB,g 或者 G 代表 GB。
您不能使用memory.memsw.limit_in_bytes 限制 root cgroup;您只能在该层级中较低的组群中应用这些值。
在memory.memsw.limit_in_bytes 中写入 -1 删除现有限制。
mount -t cgroup none /mnt/cgroup/ -omemory
查看相关的内存限制:
more memory.limit_in_bytes
9223372036854775807
more memory.memsw.limit_in_bytes
9223372036854775807
将该进程组中的内存限制开启为300MB,如下:
echo 300M > memory.limit_in_bytes
echo 300M >memory.memsw.limit_in_bytes
cat memory.limit_in_bytes
314572800
cat memory.memsw.limit_in_bytes
314572800
将当前进程的PID写入到tasks中,如下:
echo $$ > tasks
通过下面的程序,我们申请内存资源,如下:
more /tmp/test.c
#include <stdlib.h>
#define MALLOC_SIZE 1024 *1024 * 300
int main(void)
{
char *i = NULL;
long int j;
i = malloc(MALLOC_SIZE);
if(i != NULL) {
for(j = 0; j < MALLOC_SIZE;j++)
*(i+j) = 'a';
}
sleep(5);
return 0;
}
编译后运行,如下:
gcc /tmp/test.c /tmp/test
/tmp/test
Killed
memory.failcnt
报告内存达到在memory.limit_in_bytes 设定的限制值的次数。
memory.memsw.failcnt
报告内存加 swap 空间限制达到在memory.memsw.limit_in_bytes 设定的值的次数。
memory.force_empty
当设定为 0 时,会清空这个cgroup 中任务所使用的所有页面的内存。这个接口只可在 cgroup 中没有任务时使用。如果无法清空内存,则在可能的情况下将其移动到上级 cgroup 中。删除 cgroup前请使用 memory.force_empty 以避免将不再使用的页面缓存移动到它的上级 cgroup 中。
memory.swappiness
表示内核换出进程内存的倾向(swap到外部磁盘)。与 /proc/sys/vm/swappiness为整个系统设定的内核倾向中用法相同。默认值为60。低于这个值会降低内核换出进程内存的倾向,将其设定为 0 则完全不会为 cgroup 中的任务换出进程内存。高于这个值将提高内核换出进程内存的倾向,大于 100 时,内核将开始换出作为这个cgroup 中进程的地址空间一部分的页面。
请注意值为 0 不会阻止换出进程内存;系统缺少内存时仍可能发生换出内存,这是因为全局虚拟内存管理时不读取该 cgroup 值。要完全锁定页面,请使用 mlock() 而不时 cgroup。
在root cgroup中不能改变swappiness,它值在/proc/sys/vm/swappiness 中设定的 swappiness。存在子控制组的cgroup不能修改swappiness。
包含指定是否应将内存使用量计入 cgroup 层级(父控制组直至root_cgroup)的使用量。如果父辈控制组达到了内存用量限制,则会向父辈控制组及其子控制组中申请回收内存。该属性默认关闭。笔者理解默认关闭可以精确控制控制组所辖任务的内存使用,而不关注所辖控制组内存的使用情况,同时申请回收时也不会涉及其他层级的任务。
Devices子系统
# mount -t cgroup none /mnt/cgroup/-o devices
#
# ls -l /mnt/cgroup/
-r--r--r-- 1 root root 0 Jan 1 00:06 cgroup.procs
--w------- 1 root root 0 Jan 1 00:06 devices.allow
--w------- 1 root root 0 Jan 1 00:06 devices.deny
-r--r--r-- 1 root root 0 Jan 1 00:06 devices.list
-rw-r--r-- 1 root root 0 Jan 1 00:06 notify_on_release
-rw-r--r-- 1 root root 0 Jan 1 00:06 release_agent
-rw-r--r-- 1 root root 0 Jan 1 00:06 tasks
devices 子系统允许或者拒绝 cgroup 中的任务访问设备。
devices.allow
指定 cgroup 中的任务可访问的设备。每个条目有四个字段:type、major、minor 和 access。type、major 和 minor 字段中使用的值对应 Linux 分配的设备,也称 Linux 设备列表中指定的设备类型和节点数。
type
type 可以是以下三个值之一:
•a - 应用所有设备,可以是字符设备,也可以是块设备
•b - 指定块设备
•c - 指定字符设备
major, minor
major 和 minor 是由Linux 分配设备指定的设备节点数。主设备号和副设备号使用冒号分开。例如:8 是主设备号,指定 SCSI 磁盘驱动器;副设备号 1 指定第一个 SCSI 磁盘驱动器中的第一个分区;因此8:1 完整指定这个分区,对应位于 /dev/sda1 的一个文件系统。
* 可代表所有主要和次要设备节点,例如:9:*(所有 RAID 设备)或者 *:*(所有设备)。
access
access 是以下一个或者多个字母序列:
•r - 允许任务从指定设备中读取
•w - 允许任务写入指定设备
•m - 允许任务生成还不存在的设备文件
当将 access 指定为 r 时,则只能从指定设备中读取任务,但当将 access 指定为 rw时,则既可从该设备中读取任务,也可向该设备中写入任务。
devices.deny
指定 cgroup 中任务不能访问的设备。条目语法与 devices.allow 一致。
devices.list
报告为这个 cgroup 中的任务设定访问控制的设备。
# echo c 1:3 rwm > devices.allow
# cat devices.list
c 1:3 rwm
# echo c 1:3 rwm > devices.deny
# cat devices.list
Freezer子系统
mount -t cgroup none/mnt/cgroup/ -o freezer
# ls -l /mnt/cgroup/
cgroup.procs notify_on_release release_agent tasks
# mkdircd fr1/
# ls
cgroup.clone_children cgroup.event_control cgroup.procs freezer.state notify_on_release tasks
freezer 子系统挂起或者恢复 cgroup 中的任务。
freezer.state
freezer.state 有三个可能的值:
•FROZEN -- 挂起该 cgroup 中的任务。
•FREEZING -- 该系统正在挂起该cgroup 中的任务。
•THAWED -- 已经恢复该 cgroup 中的任务。
挂起进程的流程:
首先将那个进程移动到附加了freezer 子系统的层级的 cgroup 中,echo FROZEN >具体 cgroup ,挂起其中包含的进程。
恢复进程的流程:
echo THAWED >具体 cgroup ,恢复其中包含的进程。
不可能将进程移动到挂起(frozen)的 cgroup 中。
# cat freezer.state
# echo 1108 >/cgroup/freezer/fr1/tasks
# echo FROZEN >/cgroup/freezer/fr1/freezer.state
# ps
1102 root 932 D ./tst.o
# echo THAWED > /cgroup/freezer/fr1/freezer.state
#ps
1102 root 932 S ./tst.o
请注意可将 FROZEN 和 THAWED 值写入freezer.state,但无法写入 FREEZING,只能读取它。
Ns子系统
# mount -t cgroup none/mnt/cgroup/ -o ns
# ls -l /mnt/cgroup/
-r--r--r-- 1 root root 0 Jan 1 00:11 cgroup.procs
-rw-r--r-- 1 root root 0 Jan 1 00:11 notify_on_release
-rw-r--r-- 1 root root 0 Jan 1 00:11 release_agent
-rw-r--r-- 1 root root 0 Jan 1 00:11 tasks
ns 子系统提供了一个将进程分组到不同名称空间的方法。在具体名称空间中,进程可彼此互动,但会与在其它名称空间中运行的进程隔绝。这些分开的名称空间在用于操作系统级别的虚拟化时,也称之为容器。
内核是利用cgroups ns子系统对进程做了一个自动分类,相同nsproxy(即所有Namespace都相同的进程)的进程在一个cgroup,一旦通过clone创建新的Namespace,就会在当前cgroup下创建一个新的cgroup。这样以来,通过cgroup文件系统,在挂载ns 子系统的目录下,我们就可以清楚地看出Namespace的层次关系。同时操作一个进程加入或离开一个命名空间。
原文链接:http://blog.youkuaiyun.com/u014358116/article/details/22092715
作者:爱海tatao
[ 转载请保留原文出处、作者和链接。]