CGroup

Linux CGroup全称Linux Control Group, 是Linux内核的一个功能,用来限制,控制与分离一个进程组群的资源(如CPU、内存、磁盘输入输出等)。这个项目最早是由Google的工程师在2006年发起(主要是Paul Menage和Rohit Seth),最早的名称为进程容器(process containers)。在2007年时,因为在Linux内核中,容器(container)这个名词太过广泛,为避免混乱,被重命名为cgroup,并且被合并到2.6.24版的内核中去。然后,其它开始了他的发展。

Linux CGroupCgroup 可让您为系统中所运行任务(进程)的用户定义组群分配资源 — 比如 CPU 时间、系统内存、网络带宽或者这些资源的组合。您可以监控您配置的 cgroup,拒绝 cgroup 访问某些资源,甚至在运行的系统中动态配置您的 cgroup。

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主要由task,cgroup,subsystem及hierarchy构成:

task:在Cgroups中,task就是系统的一个进程。

cgroup:Cgroups中的资源控制都以cgroup为单位实现的。cgroup表示按照某种资源控制标准划分而成的任务组,包含一个或多个子系统。一个任务可以加入某个cgroup,也可以从某个cgroup迁移到另外一个cgroup。

subsystem:Cgroups中的subsystem就是一个资源调度控制器(Resource Controller)。比如CPU子系统可以控制CPU时间分配,内存子系统可以限制cgroup内存使用量。

hierarchy:hierarchy由一系列cgroup以一个树状结构排列而成,每个hierarchy通过绑定对应的subsystem进行资源调度。hierarchy中的cgroup节点可以包含零或多个子节点,子节点继承父节点的属性。整个系统可以有多个hierarchy。

cgroups子系统介绍

  1. cpu 子系统,主要限制进程的 cpu 使用率。
  2. cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。
  3. cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。
  4. memory 子系统,可以限制进程的 memory 使用量。
  5. blkio 子系统,可以限制进程的块设备 io。
  6. devices 子系统,可以控制进程能够访问某些设备。
  7. net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。
  8. net_prio — 这个子系统用来设计网络流量的优先级
  9. freezer 子系统,可以挂起或者恢复 cgroups 中的进程。
  10. ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace
  11. hugetlb — 这个子系统主要针对于HugeTLB系统进行限制,这是一个大页文件系统。

cgroups 层级结构

内核使用 cgroup 结构体来表示一个 control group 对某一个或者某几个 cgroups 子系统的资源限制。cgroup 结构体可以组织成一颗树的形式,每一棵cgroup 结构体组成的树称之为一个 cgroups 层级结构。

1.cgroups层级结构可以 attach 一个或者几个 cgroups 子系统,当前层级结构可以对其 attach 的 cgroups 子系统进行资源的限制。每一个 cgroups 子系统只能被 attach 到一个层级结构中。

2.创建了 cgroups 层级结构中的节点(cgroup 结构体)之后,可以把进程加入到某一个节点的控制任务列表中,一个节点的控制列表中的所有进程都会受到当前节点的资源限制。同时某一个进程也可以被加入到不同的 cgroups 层级结构的节点中,因为不同的 cgroups 层级结构可以负责不同的系统资源。所以说进程和 cgroup 结构体是一个多对多的关系。

上面这个图从整体结构上描述了进程与 cgroups 之间的关系。最下面的P代表一个进程。每一个进程的描述符中有一个指针指向了一个辅助数据结构css_set(cgroups subsystem set)。 指向某一个css_set的进程会被加入到当前css_set的进程链表中。一个进程只能隶属于一个css_set,一个css_set可以包含多个进程,隶属于同一css_set的进程受到同一个css_set所关联的资源限制。

上图中的”M×N Linkage”说明的是css_set通过辅助数据结构可以与 cgroups 节点进行多对多的关联。但是 cgroups 的实现不允许css_set同时关联同一个cgroups层级结构下多个节点。 这是因为 cgroups 对同一种资源不允许有多个限制配置。

一个css_set关联多个 cgroups 层级结构的节点时,表明需要对当前css_set下的进程进行多种资源的控制。而一个 cgroups 节点关联多个css_set时,表明多个css_set下的进程列表受到同一份资源的相同限制。

3.一个task不能存在于同一个hierarchy的不同cgroup,但可以存在在不同hierarchy中的多个cgroup

系统每次新建一个hierarchy时,该系统上的所有task默认构成了这个新建的hierarchy的初始化cgroup,这个cgroup也称为root cgroup。

对于你创建的每个hierarchy,task只能存在于其中一个cgroup中,即一个task不能存在于同一个hierarchy的不同cgroup中,但是一个task可以存在在不同hierarchy中的多个cgroup中。

如果操作时把一个task添加到同一个hierarchy中的另一个cgroup中,则会从第一个cgroup中移除。

如下图,cpu和memory被附加到cpu_mem_cg的hierarchy。而net_cls被附加到net hierarchy。并且httpd进程被同时加到了cpu_mem_cg hierarchy的cg1 cgroup中和net hierarchy的cg3 cgroup中。并通过两个hierarchy的subsystem分别对httpd进程进行cpu,memory及网络带宽的限制。

4.子task继承父task cgroup的关系

系统中的任何一个task(Linux中的进程)fork自己创建一个子task(子进程)时,子task会自动的继承父task cgroup的关系,在同一个cgroup中,但是子task可以根据需要移到其它不同的cgroup中。父子task之间是相互独立不依赖的。

如下图,httpd进程在cpu_and_mem hierarchy的/cg1 cgroup中并把PID 4537写到该cgroup的tasks中。

之后httpd(PID=4537)进程fork一个子进程httpd(PID=4840)与其父进程在同一个hierarchy的统一个cgroup中,但是由于父task和子task之间的关系独立不依赖的,所以子task可以移到其它的cgroup中。

数据结构

进程的task_struct字段中就有指向某个css_set的指针。其中cgroups指针指向了一个css_set结构。cg_list是一个list_head结构,用于将连到同一个css_set的进程组织成一个链表。

struct task_struct {...
struct css_set __rcu *cgroups;struct list_head cg_list;
...
}

css_set存储了与进程相关的cgroups信息

struct css_set {atomic_t refcount;struct hlist_node hlist;struct list_head tasks;struct list_head cg_links;struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];struct rcu_head rcu_head;};

其中refcount是该css_set的引用数,因为一个css_set可以被多个进程共用,只要这些进程的cgroups信息相同,比如:在所有已创建的层级里面都在同一个cgroup里的进程。

hlist用于把所有css_set组织成一个hash表,这样内核可以快速查找特定的css_set。

tasks指向所有连到此css_set的进程连成的链表。

cg_links指向一个由struct cg_cgroup_link连成的链表。

subsys是一个指针数组,存储一组指向cgroup_subsys_state的指针。通过这个指针数组,进程就可以获得相应的cgroups控制信息了。

cgroup_subsys_state存放进程与一个特定子系统相关的信息。

struct cgroup_subsys_state {struct cgroup *cgroup;atomic_t refcnt;unsigned long flags;struct css_id *id;};

cgroup指针指向了一个cgroup结构,也就是进程属于的cgroup。进程受到子系统的控制,实际上是通过加入到特定的cgroup实现的,因为cgroup在特定的层级上,而子系统又是附加到曾经上的。通过以上三个结构,进程就可以和cgroup连接起来了:task_struct->css_set->cgroup_subsys_state->cgroup。

cgroup存放子系统的相关信息

struct cgroup {unsigned long flags;    atomic_t count;struct list_head sibling; struct list_head children; struct cgroup *parent;   struct dentry *dentry;   struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];struct cgroupfs_root *root;struct cgroup *top_cgroup;struct list_head css_sets;struct list_head release_list;struct list_head pidlists;struct mutex pidlist_mutex;struct rcu_head rcu_head;struct list_head event_list;spinlock_t event_list_lock;
};

sibling,children和parent三个list_head负责将同一层级的cgroup连接成一颗cgroup树。

subsys是一个指针数组,存储一组指向cgroup_subsys_state的指针。这组指针指向了此cgroup跟各个子系统相关的信息,这个跟css_set中的道理是一样的。

root指向了一个cgroupfs_root的结构,就是cgroup所在的层级对应的结构体。

top_cgroup指向了所在层级的根cgroup,也就是创建层级时自动创建的那个cgroup。

css_set指向一个由struct cg_cgroup_link连成的链表,跟css_set中cg_links一样。

css_set和croup之间的关系如何设计,多对多的关系必须添加一个中间结构来将两者联系起来。

cg_cgroup_link就是这个中间结构

struct cg_cgroup_link {struct list_head cgrp_link_list;struct cgroup *cgrp;struct list_head cg_link_list;struct css_set *cg;};

cgrp_link_list连入到cgroup->css_set指向的链表,cgrp则指向此cg_cgroup_link相关的cgroup。

Cg_link_list则连入到css_set->cg_links指向的链表,cg则指向此cg_cgroup_link相关的css_set。

CGroup使用

查看cgroup挂载点:

创建隔离组

cd /sys/fs/cgroup/memory/
mkdir test

目录创建完成会自动生成以下文件

ls test

先写测试程序

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#define MB (1024 * 1024)int main(int argc, char *argv[])
{char *p;int i = 0;while(1) {p = (char *)malloc(MB);memset(p, 0, MB);printf("%dM memory allocated\n", ++i);sleep(1);}return 0;
}

程序很简单,无限循环,来向系统申请内存,它会每秒消耗1M的内存。

下面开始写入一些具体的参数:

sudo sh -c "echo 5M > memory.limit_in_bytes"

设置内存限额为5M

sudo sh -c "echo $$ >> cgroup.procs"

把当前bash加入到test中,即所有在此bash下创建的进程都会加入到test中

cat memory.oom_control

查看一下oom开关,一般cgroup下oom是不能关闭的。默认为0,即开启。

sudo sh -c "echo 0 > memory.swappiness"

oom会受到swap空间的影响,设置他为0,把它禁用。

接下来运行测试程序,当分配到5M时,会关闭bash。因为被kill。

我们关掉oom,这样程序就会暂停,这时来观察一下。

sudo sh -c "echo 1 >> memory.oom_control"
cat memory.oom_control

运行程序发现停在这里了

再开终端查看oom,发现under_oom为1,表示当前已经oom了。

在当前终端设置内存限额为7M.

回到第一个终端可以看到程序又开始继续分配了,最终停在了5M处

参考

https://www.cnblogs.com/lisperl/archive/2012/04/18/2455027.html

https://elixir.bootlin.com/linux/v4.8/source/Documentation/cgroup-v1/memory.txt

https://segmentfault.com/a/1190000008125359

CGroup的原理和使用相关推荐

  1. 【博客497】k8s cgroup原理完整剖析

    k8s cgroup原理 k8s cgroup设计层级: k8s完整的cgroup hierarchy root| +- kube-reserved| || +- kubelet (kubelet p ...

  2. 【重识云原生】第六章容器6.1.7.2节——cgroups原理剖析

    <重识云原生系列>专题索引: 第一章--不谋全局不足以谋一域 第二章计算第1节--计算虚拟化技术总述 第二章计算第2节--主流虚拟化技术之VMare ESXi 第二章计算第3节--主流虚拟 ...

  3. cgroup使用举例和linux内核源码详解

    cgroup的原理其实并不复杂,用法也比较简单.但是涉及的内核数据结构真的复杂,错综复杂的数据结构感觉才是cgroup真正的难点.本文结合个人学习cgroup源码的心得,尽可能以举例的形式,总结cgr ...

  4. 以firejail sandbox解析Docker核心原理依赖的四件套

    我保证这篇文章会给你一些不一样的东西,I promise. Docker大红大紫之时,我错过了什么,可能是因为我并没有必须使用Docker的动机,毕竟我不是编程者,我也不需要发布什么配置复杂的系统,我 ...

  5. dockerd、contaierd、containerd-shim、runC通信机制分析

    整体框架分析 dockerd 底层运行容器需要依赖多个二级制组件:docker daemon, containerd, container-shim, runC, 代码实现上,containerd包含 ...

  6. Linux容器:cgroup,namespace原理与实现

    转自 <容器三把斧之 | cgroup原理与实现> <容器三把斧之 | namespace原理与实现> 目录 容器三把斧之 | cgroup原理与实现 cgroup 结构体 c ...

  7. docker网络原理及cgroup

    docker网络模式的特性  docker初始状态下有三种默认的网络模式 ,bridg(桥接),host(主机),none(无网络设置) 网络模式                            ...

  8. CGroup 介绍、应用实例及原理描述【转】

    转自:https://www.cnblogs.com/caoxiaojian/p/5633430.html CGroup 介绍 CGroup 是 Control Groups 的缩写,是 Linux ...

  9. 推荐阅读-Docker实现原理之Namespace,CGroup

    找了几篇这方面的文章,写的还不错,跟大家共享: DOCKER基础技术:LINUX NAMESPACE(上) DOCKER基础技术:LINUX NAMESPACE(下) DOCKER基础技术:LINUX ...

最新文章

  1. 再谈select, iocp, epoll,kqueue及各种I/O复用机制 - Shallway - 博客频道 - CSDN.NET
  2. Linux SD卡驱动开发(六) —— SD卡启动过程总体分析
  3. 秒,在解答这个C语言题目上,我们都败了
  4. Linux查看指定进程占用mem,Linux查看占用mem的进程脚本
  5. android 数据回传代码,安卓向.net core api传输图片,执行保存到数据库命令后返回400错误代码,用postman测试没有问题安卓程序不行...
  6. ArrayList(4)时间复杂度
  7. 使用pkg打包编译nodejs程序,手动设置缓存内容
  8. C++多线程编程(2) 条件变量与原子操作
  9. 理解 TCP(二):报文结构 1
  10. SQL安装过程中安装程序挂起问题解决
  11. jetty 找不到html页面,记一次jetty 404问题排查修复
  12. 使用cmd命令行装逼,让命令行滚动起来
  13. iphone手机屏幕开发尺寸
  14. python 在图片的指定位置添加文字及图片
  15. 2017年真题精选(六)
  16. hone hone clock创意前端时钟
  17. mysql monitor怎么用,MySQL数据库 监控工具 myMySQL数据库-monitor 详解 MySQL数据库使用教程...
  18. git各种异常问题整理
  19. 凯西与拜耳将在中国共同推广呼吸药物宝丽亚和启尔畅
  20. 八、cadence ic 5141 ——反相器原理图验证

热门文章

  1. 最小二乘法求模型最优解
  2. Java选择题(十八)
  3. BinaryWriter
  4. 音频之PCM与WAV
  5. PHP代码审计--百家CMS4.1.4项目实战(下)
  6. localtimestamp
  7. 什么是数据库备份DBS
  8. 01_ROS_Introduction
  9. 缓存雪崩、缓存穿透、缓存预热、缓存更新、缓存降级等问题
  10. 寻找技术人丢失的三种气质