Linux系统中cgroup功能介绍



1  Cgroups简介

1.1 What are cgroups ?

Cgroups(控制组)Linux内核的一个功能,用来限制、统计和分离一个进程组的资源(CPU、内存、磁盘输入输出等)。换句话说就是,如果一个进程加入了某一个控制组,该控制组对Linux的系统资源都有严格的限制,进程在使用这些资源时,不能超过其最大的限制数,例如:memory资源,如果加入控制组的进程使用的memory大于其限制,可能会出现OOM错误(关于OOM错误可参看Linux内核OOM机制分析)。cgroup本身提供将进程进行分组化管理的功能和接口的基础结构

控制组提供一种机制,将一组任务和它们的子任务聚合或划分到有特定功能的层级结构(hierarchical)中。在Cgroups中涉及下面的几个概念:

1)任务(task):在 cgroups 中,任务就是系统的一个进程。

2)控制族群(control group):控制族群就是一组按照某种资源限制划分的进程。Cgroups 中的资源控制都是以控制族群为单位实现。一个进程可以加入到多个控制族群。假设这里有个进程A,我们要限制其使用的系统内存总量的10%,我们就可以创建一个memory占用10%cgroups。然后,将进程A添加到cgroups,那么进程A最多占用内存总量的10%,当然,一个cgroups一般情况下不止一个进程。

3)层级(hierarchy):控制族群可以组织成 hierarchical 的形式,既一颗控制族群树。子控制组自动继承父节点的特定属性,当然子控制组还可以有自己特定的属性。下面是Linux内核关于Cgroups的一种图示:可以看出他的层级表示:

.CPU :          "Top cpuset"

                       /       \

               CPUSet1         CPUSet2

                  |               |

               (Professors)    (Students)

 

               In addition (system tasks) are attached to topcpuset (so

               that they can run anywhere) with a limit of 20%

 

       Memory : Professors (50%), Students (30%), system (20%)

 

       Disk : Professors (50%), Students (30%), system (20%)

 

       Network : WWW browsing (20%), Network File System (60%), others (20%)

                               / \

               Professors (15%)  students (5%)

 

Browsers like Firefox/Lynx go into the WWW network class, while (k)nfsd goes

into the NFS network class.(参考Linux内核的cgroups说明

4Subsystem:子系统是任务组中的一个模块,例如前面的memory就是一个子系统,该子系统是一个内存控制器。在cgroups的框架下,为cgroups提供多种资源限制。(each subsystem has system-specific state attached to each cgroup in the hierarchy.  Each hierarchy has an instance of the cgroup virtual filesystem associated with it)。

1.2  Cgroups子系统介绍

1blkio --为块设备设定输入/输出限制,比如物理设备(磁盘,固态硬盘,USB 等)。

2cpu -- 使用调度程序提供对cgroup-bin_0.38-1ubuntu2_i386版本中的cgconfig.conf文件,但是我们可以自己创建该配置文件,这样但mount , group and default 三个段,这三个段的顺序是任意的,并且都是可选参数,该文件中用

mount section has this form:

 

              mount {

                     = ;

                     ...

              }

Controller

                   表示内核支持的子系统的名字。通过/proc/cgroups文件查看系统中支持子系统。

Path

子系统的挂载点。

                   如果没有mount段,则controllers不会被挂载。

group section has this form:

 

              group {

                     [permissions]

                     {

                            = ;

                            ...

                     }

                     ...

              }

Name

control group的名字,只能包括目录名字允许的字符。
permissions

可选项,指定cgroup对应的挂载点文件系统的权限

Controller

                   内核中支持的子系统的名字。

param nameparam value:参数的名字和参数的值。

                   如果没有group,则就没有group被创建。

Default段如下面的形式:

default {

                     perm {

                            task {

                                   uid = ;

                                   gid = ;

                                   fperm =

                            }

                            admin {

                                   uid = ;

                                   gid = ;

                                   dperm =

                                   fperm =

                            }

                     }

              }

下面举两个例子:

mount {#定义需要创建的cgroup子系统及其挂载点,这里创建cpucpuacct(统计)两个cgroup子系统

                     cpu = /mnt/cgroups/cpu;

                     cpuacct = /mnt/cgroups/cpu;

              }

和下面的shell脚本是等价的:

   mkdir /mnt/cgroups/cpu

   mount -t cgroup -o cpu,cpuacct cpu /mnt/cgroups/c

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 /mnt/cgroups/cpu

              mount -t cgroup -o cpu,cpuacct cpu /mnt/cgroups/cpu

 

              mkdir /mnt/cgroups/cpu/daemons

 

              mkdir /mnt/cgroups/cpu/daemons/www

              chown root:root /mnt/cgroups/cpu/daemons/www/*

              chown root:webmaster /mnt/cgroups/cpu/daemons/www/tasks

              echo 1000 > /mnt/cgroups/cpu/daemons/www/cpu.shares

                   上面的具体内容可以参考,cfconfig.conf文件说明

1.4 用户空间挂载Cgroup

                   上面我们分析了一下,cgroup的配置文件,这里我们具体分析一下使用命令如何进行配置。我们知道cgroup的实现有一个文件系统的支持,用户空间的配置可以使用cgroup文件系统。首先,我们可以创建一个层级:在创建层级之前我们需要创建挂载的目录,这里使用的目录是/home/ubuntu /test_cgroup

root@ubuntu:~# mount -t cgroup -o cpu cpu_group /home/ubuntu/test_cgroup/

这个命令就是创建了一个test_cgroup的层级,这个层级有一个子系统cpu,并把层级挂载到了/home/ubuntu /test_cgroup目录下。进入该目录可以看到一些和CPU相关的参数。

root@ubuntu:~# cd test_cgroup/

root@ubuntu:~/test_cgroup# ls

cgroup.clone_children  cpu.cfs_period_us  cpu.shares         tasks

cgroup.event_control   cpu.cfs_quota_us   cpu.stat       cgroup.procs           cpu.rt_period_us   notify_on_release

cgroup.sane_behavior   cpu.rt_runtime_us  release_agent

然后创建一个cgroup,名字为foo

root@ubuntu:~/test_cgroup# mkdir foo

root@ubuntu:~/test_cgroup# cd foo/

root@ubuntu:~/test_cgroup/foo# ls

cgroup.clone_children  cpu.cfs_period_us  cpu.rt_runtime_us  notify_on_release

cgroup.event_control   cpu.cfs_quota_us   cpu.shares         tasks

cgroup.procs           cpu.rt_period_us   cpu.stat

             这样就在cpu_group层级上创建了一个foocgroup,在Foo目录下有一些文件,这些都是CPU相关的属性。其中cat ~/test_cgroup/task可以看到系统中的所有进程ID。这是因为每创建一个层级,系统会把系统中所以的进程ID添加到task中,而在foo/task文件却是空,这个文件用户可以添加。

/proc/下面有一个cgroups的文件,记录了所有cgroups子系统的状态信息。

root@ubuntu:~/test_cgroup/foo# cat /proc/cgroups

#subsys_name    hierarchy num_cgroups            enabled

cpuset                   3                 1                 1

cpu      4                 3                 1

cpuacct                 5                 1                 1

memory                6                 2                 1

devices                  7                 1                 1

freezer                  8                 1                 1

blkio    9                 1                 1

perf_event           10              1                 1

hugetlb                  11              1                 1

             从左到右的条目分别是子系统namehierarchy ID,子系统的cgroup控制组数目,子系统是否可用(1可用,0不可用)

/proc/mounts文件显示了系统中挂载的文件系统信息,前面几个目录表示文件系统name,挂载点绝对路径,文件系统类型

root@ubuntu:~/test_cgroup/foo# cat /proc/mounts

rootfs / rootfs rw 0 0

sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0

proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0

udev /dev devtmpfs rw,relatime,size=503368k,nr_inodes=125842,mode=755 0 0

devpts /dev/pts devpts rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000 0 0

tmpfs /run tmpfs rw,nosuid,noexec,relatime,size=102556k,mode=755 0 0

/dev/disk/by-uuid/612dacd9-3cf7-471b-92a4-f5c7c6e4e88a / ext4 rw,relatime,errors=remount-ro,data=ordered 0 0

none /sys/fs/cgroup tmpfs rw,relatime,size=4k,mode=755 0 0

none /sys/fs/fuse/connections fusectl rw,relatime 0 0

none /sys/kernel/debug debugfs rw,relatime 0 0

none /sys/kernel/security securityfs rw,relatime 0 0

none /run/lock tmpfs rw,nosuid,nodev,noexec,relatime,size=5120k 0 0

none /run/shm tmpfs rw,nosuid,nodev,relatime 0 0

none /run/user tmpfs rw,nosuid,nodev,noexec,relatime,size=102400k,mode=755 0 0

none /sys/fs/pstore pstore rw,relatime 0 0

systemd /sys/fs/cgroup/systemd cgroup rw,nosuid,nodev,noexec,relatime,name=systemd 0 0

vmware-vmblock /run/vmblock-fuse fuse.vmware-vmblock rw,nosuid,nodev,relatime,user_id=0,group_id=0,default_permissions,allow_other 0 0

gvfsd-fuse /run/user/1000/gvfs fuse.gvfsd-fuse rw,nosuid,nodev,relatime,user_id=1000,group_id=1000 0 0

cgroup /sys/fs/cgroup/cpuset cgroup rw,relatime,cpuset 0 0

cgroup /sys/fs/cgroup/cpu cgroup rw,relatime,cpu 0 0

cgroup /sys/fs/cgroup/cpuacct cgroup rw,relatime,cpuacct 0 0

cgroup /sys/fs/cgroup/memory cgroup rw,relatime,memory 0 0

cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0

cgroup /sys/fs/cgroup/freezer cgroup rw,relatime,freezer 0 0

cgroup /sys/fs/cgroup/blkio cgroup rw,relatime,blkio 0 0

cgroup /sys/fs/cgroup/perf_event cgroup rw,relatime,perf_event 0 0

cgroup /sys/fs/cgroup/hugetlb cgroup rw,relatime,hugetlb 0 0

gvfsd-fuse /root/.gvfs fuse.gvfsd-fuse rw,nosuid,nodev,relatime,user_id=0,group_id=0 0 0

test_group /home/zfz/cgroup cgroup rw,relatime,cpu 0 0

cpu_group /home/zfz/test_cgroup cgroup rw,relatime,cpu 0 0

1.5  Cgroup使用实例

lssubsys -al来列出系统支持多少种子系统,和使用ls /sys/fs/cgroup/ ubuntu)来显示已经挂载的子系统。

如果提示下面的错误信息,则表示系统中还没有安装cgroup.

lssubsys -all

The program 'lssubsys' is currently not installed. You can install it by typing:

                   需要使用下面的方法进行安装:

root@ubuntu:~# apt-get install cgroup-bin

Reading package lists... Done

                   再次使用lssubsys列出系统中支持的子系统,根据不同的内核版本,支持的子系统也不相同。

root@ubuntu:~# lssubsys --all

cpuset

cpu

cpuacct

memory

devices

freezer

blkio

perf_event

hugetlb

                   还有一种情况需要进行确认,确认相应的内核部分是否已经编译。

grep CGROUP /boot/config-`uname -r`

CONFIG_CGROUPS=y

# CONFIG_CGROUP_DEBUG is not set

CONFIG_CGROUP_FREEZER=y

CONFIG_CGROUP_DEVICE=y

CONFIG_CGROUP_CPUACCT=y

CONFIG_CGROUP_HUGETLB=y

CONFIG_CGROUP_PERF=y

CONFIG_CGROUP_SCHED=y

CONFIG_BLK_CGROUP=y

# CONFIG_DEBUG_BLK_CGROUP is not set

CONFIG_NET_CLS_CGROUP=m

CONFIG_NETPRIO_CGROUP=m

                   我们可以创建一个cgroupcpu子系统,创建完成之后就在该目录下有下面的属性文件可以进行设置。

   root@ubuntu:/sys/fs/cgroup/cpu/test_cpu# ls

cgroup.clone_children  cpu.cfs_period_us  cpu.rt_runtime_us  notify_on_release

cgroup.event_control   cpu.cfs_quota_us   cpu.shares         tasks

cgroup.procs           cpu.rt_period_us   cpu.stat

                   使用cat命令查看其中的默认值。

root@ubuntu:/sys/fs/cgroup/cpu/test_cpu# cat cpu.cfs_quota_us

-1

root@ubuntu:/sys/fs/cgroup/cpu/test_cpu# cat cpu.cfs_period_us

100000

默认挂载在/sys/fs/cgroup/memory,可以通过cfconfig.conf文件查看默认的挂载点。这里有两个小的例子主要是为了测试cgroup对进程使用CPUmemeory的控制,这里使用两个Python脚本来模拟其中的效果。

例子1:设置该进程对CPU的占用率不能超过50%

root@ubuntu:/sys/fs/cgroup/cpu/test_cpu# echo 50000 > cpu.cfs_quota_us

root@ubuntu:/sys/fs/cgroup/cpu/test_cpu# cat cpu.cfs_quota_us

50000

root@ubuntu:/sys/fs/cgroup/cpu/test_cpu# cat tasks

root@ubuntu:/sys/fs/cgroup/cpu/test_cpu# echo 9477 > tasks

使用的Python程序如下:

root@ubuntu:~# cat python_test_cpu.py

#!/usr/bin/env python

import os

pid_num = os.getpid()

print pid_num

var = 1

count = 1

while var == 1:

    count = count + 1

count = 0

在没有把python的进程号添加到tasks中,该进程的CPU占用率接近95%。而把该进程ID添加到tasks中经过持续的观察,该进程的CPU使用率不超出50%,和在cgroup中设置的一致。


例子2:限制内存的使用率

                   Python代码如下:

root@ubuntu:~# cat python_test_memory.py

#!/usr/bin/env python

import os

import time

pid_num = os.getpid()

print pid_num

var = 1

a = "abcdefgh"

b = "abcdefgh"

c = ""

time.sleep(10)

#while var == 1:

for i in range(1,30000000):

    c = "####".join([a,b]) + c

                   在没有使用cgroups机制的情况下,通过top命令查看该进程占用的memory情况如下:


持续观察一段时间基本维持在40%左右。在使用cgoup机制,限制在10M的情况下,其内存的占有率发生了变化。

root@ubuntu:/sys/fs/cgroup/memory/memory_test# echo 10M > memory.limit_in_bytes

root@ubuntu:/sys/fs/cgroup/memory/memory_test# cat memory.limit_in_bytes

10485760


                   过一小段时间会出现进程退出的情况,如下:进程被杀掉了。

root@ubuntu:~# ./python_test_memory.py

10608

[8]+  Killed                  ./python_test_memory.py

Killed

 在这里也可以配置内核的OOM机制,默认值是开着呢。

root@ubuntu:/sys/fs/cgroup/memory/memory_test# cat memory.oom_control

oom_kill_disable 0

under_oom 0

             在上面我们设置的是memory.limit_in_bytes 值,但在目录下还有一个memory.soft_limit_in_bytes 属性,,该值和 memory.limit_in_bytes 的差异是,memory.soft_limit_in_bytes这个限制并不会阻止进程使用超过限额的内存,只是在系统内存不足时,会优先回收超过限额的进程占用的内存,使之向限定值靠拢

通过上面的分析可以看出来,简单的说Cgroup就是限制了各个不同进程之间使用各种资源的能力。上面两个例子是最常用的CPU和内存,每一个内核子系统都有很多的配置选项信息,其中配置选项的含义可以参考Linux内核文档,LinuxCgroup子系统解释Cgroup做到对资源的限制访问,这对于Linux系统上的虚拟化和云计算提供了一种资源配置方式。

参考文献:

http://blog.chinaunix.net/uid-20940095-id-3294134.html

http://files.cnblogs.com/lisperl/cgroups介绍.pdf

展开阅读全文

没有更多推荐了,返回首页