您的位置:首页 > 运维架构 > Linux

cgroups分析与应用连载(一)

2014-03-25 20:05 288 查看

欢迎转载,转载请参见文章末尾处要求

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的后代)的初始成员。

2.一个子系统最多只能附加到一个层级。

# 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功能。

# echo 1086 > /tmp/tmp1/tasks

# cat  /tmp/tmp1/tasks

 

为什么没有生效?看看是不是没有完成mems和cpus的初始化。

 

顶层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 <stdio.h>

#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。

 

memory.use_hierarchy

包含指定是否应将内存使用量计入 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

THAWED

# 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.csdn.net/u014358116/article/details/22092715

作者:爱海tatao 

[ 转载请保留原文出处、作者和链接。]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息