标签:不同的 权重 核心 rgb 大内存 cpu 负载 多核 libc 虚拟化
cgroups 的全称是control groups,是Linux内核提供的一种可以限制、记录、隔离进程组所使用的物理资源(包括:CPU、memory、IO等),可以对 cpu,内存等资源实现精细化的控制,目前越来越火的轻量级容器 Docker 就使用了 cgroups 提供的资源限制能力来完成cpu,内存等部分的资源控制。
限制进程组可以使用的资源(Resource limiting ):比如memory子系统可以为进程组设定一个memory使用上限,进程组使用的内存达到限额再申请内存,就会出发OOM(out of memory)
进程组的优先级控制(Prioritization ):比如可以使用cpu子系统为某个进程组分配cpu share
记录进程组使用的资源量(Accounting ):比如使用cpuacct子系统记录某个进程组使用的cpu时间
进程组隔离(Isolation):比如使用ns子系统可以使不同的进程组使用不同的namespace,以达到隔离的目的,不同的进程组有各自的进程、网络、文件系统挂载空间
进程组控制(Control):比如使用freezer子系统可以将进程组挂起和恢复
cgroups为每种可以控制的资源定义了一个子系统。典型的子系统介绍如下:
cpu 子系统,主要限制进程的 cpu 使用率。
cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告。 cpuset 子系统,可以为 cgroups 中的进程分配单独的 cpu 节点或者内存节点。 memory 子系统,可以限制进程的 memory 使用量。 blkio 子系统,可以限制进程的块设备 io。 devices 子系统,可以控制进程能够访问某些设备。 net_cls 子系统,可以标记 cgroups 中进程的网络数据包,然后可以使用 tc 模块(traffic control)对数据包进行控制。 freezer 子系统,可以挂起或者恢复 cgroups 中的进程。 ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace。
如图所示,CPU 和 Memory 两个子系统有自己独立的层级系统,而又通过 Task Group 取得关联关系
内存memory子系统,mm_struct结构体激励所属的cgroup,当进程需要申请更多的内存时,就会触发cgroup用量检测,用量超过cgroup规定的限额,则拒绝用户的内存申请
3.1、task_struct 结构体
从进程的角度出发来剖析 cgroups 相关数据结构之间的关系。在 Linux 中管理进程的数据结构是 task_struct。cgroup表示进程的行为控制.因为subsys必须要知道进程是位于哪一个cgroup,所以在struct task_struct和cgroup中存在一种映射
struct task_struct { …… …… #ifdef CONFIG_CGROUPS /* Control Group info protected by css_set_lock */ struct css_set *cgroups; /* cg_list protected by css_set_lock and tsk->alloc_lock */ struct list_head cg_list; #endif …… …… }
cgroups 指针指向了一个 css_set (cgroups subsystem set)结构,而css_set 存储了与进程有关的 cgroups 信息。cg_list 是一个嵌入的 list_head 结构,用于将连到同一个 css_set 的进程组织成一个链表。
3.2、css_set结构体
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; };
3.3、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。
3.4、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; };
3.5、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;
};
一个进程对应一个 css_set,一个 css_set 存储了一组进程 (有可能被多个进程共享,所以是一组) 跟各个子系统相关的信息,但是这些信息由可能不是从一个 cgroup 那里获得的,
因为一个进程可以同时属于几个 cgroup,只要这些 cgroup 不在同一个层级。
VFS虚拟文件系统转换,处理与Unix标准文件系统的所有系统调用。VFS对用户提供统一的读写接口,用户调用读写等函数时,内核则调用特定的文件系统实现。文件在内核内存中是一个file数据结构来表示的。这个数据结构包含一个f_op的字段,该字段中包含了一组指向特定文件系统实现的函数指针。当用户执行read()操作时,内核调用sys_read(),然后sys_read()查找到指向该文件属于的文件系统的读函数指针,并调用它,即file->f_op->read().
基于VFS实现的文件系统,都必须实现定义这些对象,并实现这些对象中定义的函数指针。
cgroup文件系统的定义:
static struct file_system_type cgroup_fs_type = { .name = "cgroup", .get_sb = cgroup_get_sb, .kill_sb = cgroup_kill_sb, };
这里有定义了两个函数指针,定义了一个文件系统必须实现了的两个操作get_sb,kill_sb,即获得超级块和释放超级块。这两个操作会在使用mount系统调用挂载cgroup文件系统时使用。
5.1、
CPU资源控制
CPU资源的控制也有两种策略
一种是完全公平调度 (CFS:Completely Fair Scheduler)策略,提供了限额和按比例分配两种方式进行资源控制; 另一种是实时调度(Real-Time Scheduler)策略,针对实时进程按周期分配固定的运行时间。配置时间都以微秒(μs)为单位,文件名中用us表示。
cpu.rt_period_us :设定周期时间。
cpu.rt_runtime_us:设定周期中的运行时间。
5.1、cpuacct资源报告
这个子系统的配置是cpu
子系统的补充,提供CPU资源用量的统计,时间单位都是纳秒。
cpuacct.usage:统计cgroup中所有task的cpu使用时长
cpuacct.stat:统计cgroup中所有task的用户态和内核态分别使用cpu的时长
cpuacct.usage_percpu:统计cgroup中所有task使用每个cpu的时长
5.2、cpuset
为task分配独立CPU资源的子系统,参数较多,这里只选讲两个必须配置的参数,同时Docker中目前也只用到这两个。
cpuset.cpus:可使用的CPU编号,如0-2,16代表 0、1、2和16这4个CPU cpuset.mems:与CPU类似,表示cgroup可使用的memory node,格式同上,NUMA系统使用
5.3、device ( 限制task对device的使用)
设备黑/白名单过滤
devices.allow:允许名单,语法type device_types:node_numbers access type ;type有三种类型:b(块设备)、c(字符设备)、a(全部设备);access也有三种方式:r(读)、w(写)、m(创建)
devices.deny:禁止名单,语法格式同上
统计报告
devices.list:报???告???为???这???个??? cgroup 中???的?task设???定???访???问???控???制???的???设???备
5.4、freezer - 暂停/恢复cgroup中的task
只有一个属性,表示进程的状态,把task放到freezer所在的cgroup,再把state改为FROZEN,就可以暂停进程。不允许在cgroup处于FROZEN状态时加入进程。freezer.state 包括如下三种状态:
5.5、memory (内存资源管理)
memory.limit_bytes:强制限制最大内存使用量,单位有k、m、g三种,填-1则代表无限制
memory.soft_limit_bytes:软限制,只有比强制限制设置的值小时才有意义
memory.memsw.limit_bytes:设定最大内存与swap区内存之和的用量限制
memory.oom_control: 0表示开启,当cgroup中的进程使用资源超过界限时立即杀死进程。默认包含memory子系统的cgroup都启用。当oom_control不启用时,实际使用内存超过界限时进程会被暂停直到有空闲的内存资源
统计
memory.usage_bytes:报???告???该??? cgroup中???进???程???使???用???的???当???前???总???内???存???用???量(以字节为单位) memory.max_usage_bytes:报???告???该??? cgroup 中???进???程???使???用???的???最???大???内???存???用???量 memory.failcnt:报???告???内???存???达???到???在??? memory.limit_in_bytes设???定???的???限???制???值???的???次???数??? 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 限???制???,单???位???为???字???节???
6.1、通过 --cpus 选项指定容器可以使用的 CPU 个数,这个还是默认设置了cpu.cfs_period_us(100000)和cpu.cfs_quota_us(200000)
docker run -it --rm --cpus 2 harbor.master.online.local/library/centos:7.2-base /bin/bash
登陆容器执行命令: yum install -y epel-release rpm --rebuilddb yum install -y stress
容器中执行: stress -c 5,查看容器使用资源情况,容器 CPU 的负载为 200%,理解为单个 CPU 负载的两倍。也可以把它理解为有两颗 CPU 在 100% 工作
宿主机top查看,5个 CPU 的负载都是 40%,加起来容器消耗的 CPU 总量就是两个 CPU 100% 的负载,不过CPU多了,靠系统来调度分配CPU
总结: 对于进程没有 CPU 个数概念,内核只能通过进程消耗的 CPU 时间片来统计出进程占用 CPU 的百分比。
6.2、指定固定的 CPU
通过 --cpuset-cpus 选项可以固定到指定CPU,现在的多核系统中每个核心都有自己的缓存,如果频繁的调度进程在不同的核心上执行势必会带来缓存失效等开销
运行容器: docker run -it --rm --cpuset-cpus=1 harbor.master.online.local/library/centos:7.2-base /bin/bash 安装stress: yum install -y epel-release && rpm --rebuilddb && yum install -y stress 执行压测:stress -c 5
查看容器状态:
宿主机查看cpu占用情况,五个进程占用一个指定的CPU
根据Docker布道师Jerome Petazzoni的说法,Docker约等于LXC+AUFS(AUFS 是一种 Union File System(联合文件系统))(之前只支持ubuntu时)(作者2015-10-22更新:Docker0.9.0版本开始引入libcontainer,可以视作LXC的替代品)。其中LXC负责资源管理,AUFS负责镜像管理;而LXC包括cgroup、namespace、chroot等组件,并通过cgroup进行资源管理。所以只从资源管理这条线来看的话,Docker、LXC、Cgroup三者的关系是:Cgroup在最底层落实资源管理,LXC在cgroup上封装了一层,Docker又在LXC封装了一层,关系图如图1(b)所示。因此,要想玩转Docker,有必要了解负责资源管理的CGroup和LXC。
LXC(LinuxContainer)容器可以提供轻量级的虚拟化,以便隔离进程和资源,而且不需要提供指令解释机制以及全虚拟化的其他复杂性。容器有效地将由单个操作系统管理的资源划分到孤立的组中,以更好地在孤立的组之间平衡有冲突的资源使用需求。
LXC建立在CGroup基础上,我们可以粗略的认为LXC = Cgroup+ namespace + Chroot + veth +用户态控制脚本。LXC利用内核的新特性(CGroup)来提供用户空间的对象,用来保证资源的隔离和对于应用或者系统的资源控制。
标签:不同的 权重 核心 rgb 大内存 cpu 负载 多核 libc 虚拟化
原文地址:https://www.cnblogs.com/liugp/p/14854317.html