cgroup从入门到懵圈——cgroup虚拟化概念

51 篇文章 2 订阅
11 篇文章 0 订阅

    从今天起,我要开始写博客了。先立个flag在这儿:两周一篇。万事开头难,中间不容易,最后会放弃。请各位看官监督(虽然似乎肯定没有人看)。

   之前有接触过cgroup,但东西都是同事在做(羡慕),理解不深。所以这个系列的文档,我假装自己是cgroup菜鸡(其实我不需要假装),从零开始学习cgroup。

    cgroup是linux内核实现、用于控制linux系统资源的组件。因此要了解cgroup,首先从引入这个组件的kernel文档中寻找。去到举世闻名的www.kernel.org网站寻找,嘿,找到介绍文档:Documentation/cgroup-v1/cgroups.txt

    1 什么是cgroup?

    cgroup ,控制组,它提供了一套机制用于控制一组特定进程对资源的使用。cgroup绑定一个进程集合到一个或多个子系统上。

    subsystem,子系统,一个通过cgroup提供的工具和接口来管理进程集合的模块。一个子系统就是一个典型的“资源控制器”,用来调度资源或者控制资源使用的上限。其实每种资源就是一个子系统。子系统可以是以进程为单位的任何东西,比如虚拟化子系统、内存子系统。

    hierarchy,层级树,多个cgroup的集合,这些集合构成的树叫hierarchy。可以认为这是一个资源树,附着在这上面的进程可以使用的资源上限必须受树上节点(cgroup)的控制。hierarchy上的层次关系通过cgroupfs虚拟文件系统显示。系统允许多个hierarchy同时存在,每个hierachy包含系统中的部分或者全部进程集合。

    cgroupfs是用户管理操纵cgroup的主要接口:通过在cgroupfs文件系统中创建目录,实现cgroup的创建;通过向目录下的属性文件写入内容,设置cgroup对资源的控制;向task属性文件写入进程ID,可以将进程绑定到某个cgroup,以此达到控制进程资源使用的目的;也可以列出cgroup包含的进程pid。这些操作影响的是sysfs关联的hierarchy,对其它hierarchy没有影响。

    对于cgroup,其本身的作用只是任务跟踪。但其它系统(比如cpusets,cpuacct),可以利用cgroup的这个功能实现一些新的属性,比如统计或者控制一个cgroup中进程可以访问的资源。举个例子,cpusets子系统可以将进程绑定到特定的cpu和内存节点上。

    2 为什么需要cgroup?

    这个问题相当于问cgroup重要吗?有哪些地方用到了。回答是重要,又不重要。如果你用到了,那就重要,如果没有用到,那就不重要。呵呵呵~~~~其实挺重要的。cgroup的主要运用是资源跟踪。我接触的场景就是用cgroup控制虚拟机进程或者docker进程可以使用的资源。当你想在linux对应用进程做资源访问控制的时候,cgroup就派上用场了。

    3 cgroup怎么实现的?

    ——    系统中的每个进程(task_struct,后面用task代指)都持有一个指向css_set结构的指针。

    ——    一个css_set结构体包含了一组指向cgroup_subsys_state对象的指针(所以一个task可以附加到多个cgroup上),每个cgroup_subsys_state在系统中都有注册。task结构体没有直接指向hierarchy中一个节点(cgroup)的指针。但可以通过其包含的cgroup_subsys_state间接确定。这样设计的原因是cpu对subsystem state的访问很频繁,但涉及到将task绑定到cgroup的操作却不多。task中还有个双向链表cg_list,这个链表维护所有同属于一个css_set的tasks。

    ——    用户可以通过cgroupfs文件系统来浏览cgroup hierarchy。

    ——    用户可以列出任意一个cgroup上附着的task PID

    cgroup在kernel中除了本身功能的实现外,在kernel中还有两处修改:

    ——    在kernel启动时对root cgroup的初始化和css_set结构体的初始化。这个在init/main.c文件中实现。

    ——    在task的创建(fork)和退出(exit)阶段,对应地将task与css_set进行绑定和解绑。

    另外,cgroup为了向用户提供操作接口,特别开发了一个虚拟文件系统类型(cgroupfs),这个文件系统与sysfs,proc类似。cgroupfs是向用户展示cgroup的hierarchy,通知kernel用户对cgroup改动的窗口。挂载cgroupfs时通过选项(-otype)指定要挂载的子系统类型,如果不指定,默认挂载所有的注册的子系统。

    如果新挂载的cgroup关联的hierachy与系统中存在的hierarchy完全一样,那么cgroupfs会拒绝挂载。如果没有匹配到相同的hierarchy,但新挂载hierachy声明的资源正在被已经存在的hierarchy使用,挂载会报-EBUSY错误。

    当前cgroup还没有实现向已经存在的cgroup hierarchy绑定新子系统的操作,将子系统从cgroup hierachy解绑也不允许。这些操作在未来也许会支持,但也可能会进一步产生错误恢复的一系列问题。

    卸载cgroupfs时,如果它的子cgroupfs还在活动,那么子cgroupfs还是会持续生效。直到所有的子cgroupfs不再活动,卸载cgroupfs才会真正生效。

    cgroupfs下不能再挂载其它类型的文件系统。所有对cgroup的查询修改都只通过cgroupfs文件系统来完成。

    系统中的所有task,在/proc/pid目录下都有一个名为cgroup的文件,这个文件展示了该task相对cgroupfs 根的路径。通过查看这个文件,可以了解一个进程在cgroup hierarchy的位置。以此得到task可以使用的资源信息。

    cgroupfs中目录表示cgroup,每个目录在创建时默认生成如下的属性文件,这些文件描述了cgroup的信息:

    ——    tasks: 所有附属于这个cgroup的进程ID列表。tasks文件中增加进程ID,表示将进程加入这个cgroup,进程能够使用的资源受cgroup限制。

    ——    cgroup.procs: 所有附属于这个cgroup线程组ID,将TGID写入这个文件后,TGID所在进程包含的所有线程都加入这个cgroup,这些线程受cgroup限制。

    PID:这是 Linux 中在其命名空间中唯一标识进程而分配给它的一个号码,称做进程ID号,简称PID。在使用 fork 或 clone 系统调用时产生的进程均会由内核分配一个新的唯一的PID值。
    TGID:在一个进程中,如果以CLONE_THREAD标志来调用clone建立的进程就是该进程的一个线程,它们处于一个线程组,该线程组的ID叫做TGID。处于相同的线程组中的所有进程都有相同的TGID;线程组组长的TGID与其PID相同;一个进程没有使用线程,则其TGID与PID也相同。
    PGID:另外,独立的进程可以组成进程组(使用setpgrp系统调用),进程组可以简化向所有组内进程发送信号的操作,例如用管道连接的进程处在同一进程组内。进程组ID叫做PGID,进程组内的所有进程都有相同的PGID,等于该组组长的PID。
    SID:几个进程组可以合并成一个会话组(使用setsid系统调用),可以用于终端程序设计。会话组中所有进程都有相同的SID。

    ——    notify_on_release flag: 标记退出时是否运行release agent

    ——    release_agent: 制定要运行的release agent的路径,这个属性文件只在cgroup的顶层目录中存在。

    以上文件是每个cgroup基本的属性文件,对于不同的子系统,对应的cgroup可能会有其它附加的属性文件,存在于其对应的cgroup目录之下。

    通过mkdir命令创建cgroup,通过向目录下的文件写入适当的数值设置修改cgroup的属性。

    嵌套的cgroups,指定了层级结构,以此将系统资源划分成嵌套的,动态可变的更小的资源块。

    一个进程可以附加到多个不同的cgroup中,只要这些cgroup不在同一个层级树上即可。因为cgroupfs会保证新挂载的cgroup关联的层级树全局唯一。子进程在被创建后默认附加到父进程所在的cgroup,后面用户可以根据需要将其移动到别的cgroup。

    当进程从一个cgroup被移动到另一个cgroup。进程的task_struct会获取一个新的css_set指针:如果这个cgroup所在的css_set已经存在就重用这个css_set,否则就新分配一个css_set。kernel会在全局的hash表中查找确认cgroup所属的css_set是否存在。

    4 notify_on_release 是做什么的?

    如果cgroup中使能notify_on_release,cgroup中的最后一个进程被移除,最后一个子cgroup也被删除时,cgroup会主动通知kernel。接收到消息的kernel会执行release_agent文件中指定的程序。notify_on_release默认是关闭的,release_agent的内容默认为空,子cgroup在创建时会继承父cgroup中notify_on_relase和release_agent的属性。所以这两个文件只存在于cgroupfs的顶层目录中。

    5 clone_children有什么用?

    clone_chilren仅针对cpu绑定(cpuset),如果clone_children使能,新的cpuset cgroup在初始化时会继承父cgroup的属性。

    6 cgroup怎么用?

    假设现在要将一个新的任务加入到cgroup,功能是将该任务的进程在指定的cpu上运行,因此我们使用"cpuset"cgroup 子系统,操作的大致步骤如下:

    1)mount -t tmpfs cgroup_root /sys/fs/cgroup

    挂载cgroup根文件系统,类型为tmpfs

    2)mkdir /sys/fs/cgroup/cpuset

    在cgroupfs根目录下创建子cgroup,名为cpuset

    3)mount -t cgroup -o cpuset cpuset /sys/fs/cgroup/cpuset

    将名为cpuset的cgroup关联到cpuset子系统

    4)在cpuset目录下创建目录,生成一个子cgroup,属性文件中写入相应内容,设置属性。

    5)启动需要限制的进程,查找其对应的进程ID,将其写入对应的task文件中

    以下操作步骤是创建一个名为"Charlie"的cgroup,这个cgroup的资源包含cpu2,cpu3和内存节点1,将shell进程附加到这个cgroup。

    mount -t tmpfs cgroup_root /sys/fs/cgroup

    mkdir /sys/fs/cgroup/cpuset

    mount -t cgroup cpuset -o cpuset /sys/fs/cgroup/cpuset

    cd /sys/fs/cgroup/cpuset

    mkdir Charlie

    cd Charlie

    echo 2-3 > cpuset.cpus

    echo 1 > cpuset.mems

    echo $$ > tasks

    sh

    cat /proc/self/cgroup
---------------------

    1 基本用法

    通过cgroup虚拟文件系统可以创建,修改,使用cgroup。

    挂载一个关联所有子系统的cgroup 层级树可以使用以下命令:

    mount -t cgroup xxx /sys/fs/cgroup

    xxx不会被cgroup的代码处理,但是会呈现到/proc/mounts或者mount命令输出中。

    注意:有些子系统在用户配置之前不会生效。比如cpusets子系统,它必须在cpus和mems两个文件被配置后才能生效,所以设置的时候,一定要先配置cpuset.cpus和cpuset.mems两个文件后再将目标进程ID写入tasks文件。

    cgroup建议对于不同的资源使用不同的层级树控制,对于用户想限制的资源,也建议使用独立的层级树进行控制。因此,通常的做法是将/sys/fs/cgroup挂载为tmpfs文件系统,在其下的每个目录,依次挂载独立的cgroup层级树关联不同类型的资源。比如:

    mount -t tmpfs cgroup_root /sys/fs/cgroup

    mkdir /sys/fs/cgroup/rg1

    挂载cpuset和memory子系统:

    mount -t cgroup -o cpuset,memory hier1 /sys/fs/cgroup/rg1

    cgroup文件系统可以支持重复挂载,比如在挂载了cpuset,memory之后:    

    mount -t cgroup -o blkio hier1 /sys/fs/cgroup/rg1

    cgroup重复挂载需要改变子系统,完全一样的子系统不允许重复挂载。

    通过下面的语法可以在挂载时指定release_agent:

    mount -t cgroup -o cpuset,release_agent="/sbin/cpuset_release_agent" xxx /sys/fs/cgroup/rg1

    挂载命令多次指定release_agent会报错。

    在/sys/fs/cgroup/cg1目录下,可以找目录树对应这个系统的cgroup。比如,/sys/fs/cgroup/rg1就是整个系统的cgroup。

    如果想要修改release_agent的内容,通过如下命令进行:

    echo "/sbin/new_release_agent" > /sys/fs/cgroup/rg1/release_agent

    通过重新挂载cgroup,设置release_agent也可以达到目录

    在/sys/fs/cgroup/rg1下面创建新的cgroup:

    cd /sys/fs/cgroup/rg1

    mkdir my_cgroup

    设置cgroup

    cd my_cgroup

    echo $$ > tasks

    进一步的,可以在这个目录下创建新的子cgroup。

    mkdir my_sub_cs

    删除cgroup,通过rmdir命令实现

    如果cgroup处在运行状态,删除操作会报错,只有当tasks中包含的进程生命结束,对应的cgroup才能删除。

    2 添加任务进程到cgroup

    echo PID > tasks

    注意是PID不是PIDs。一次只能添加一个任务进程ID。如果有多个任务ID,分多次添加。

    把当前shell的进程ID添加到cgroup:

    echo 0 > tasks

    将TGID或者线程组中包含的任意线程ID添加到cgroup.procs中,可以实现将线程组所有线程添加到cgroup。往cgroup.procs文件中写0表示将写入进程所在进程组所有进程都添加到cgroup中。

    注意:cgroup各个子系统初始化时,默认把系统中所有进程都纳管了。将一个进程的PID添加到新建的cgroup tasks文件的操作,实际是从一个cgroup移入另一个cgroup的操作。所以要将进程从某个cgroup中删除,只能通过将其移出到另一个cgroup来实现,或者直接将进程终止。
---------------------
作者:享乐主
来源:CSDN
原文:https://blog.csdn.net/huang987246510/article/details/80820355
版权声明:本文为博主原创文章,转载请附上博文链接!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值