(备注: 不显示声明就是基于V1版本来讲解的)
- 1 什么是 cgroups
- 2 为什么我们需要 cgroups
- 3 crgoups 是如何实现的
- 4 如何使用 cgroups
- 5 `cgroup V2`版本有什么不一样
- 6 总结
1 什么是 cgroups
1.1 基本概念
cgroups
机制是用来限制一个进程或者多个进程的资源。
概念:
Subsystem
(子系统): 每种可以控制的资源都被定义成一个子系统,比如CPU子系统
,Memory子系统
。Control Group
(控制组):cgroup
是用来对一个subsystem
(子系统)或者多个子系统的资源进行控制。Hierarchy
(层级):Control group
使用层次结构 (Tree
) 对资源做划分。参考下图:
每个层级都会有一个根节点, 子节点是根节点的比重划分。
关系:
- 一个子系统最多附加到一个层级(Hierarchy)上。
- 一个层级(Hierarchy)可以附加多个子系统。
1.2 进程和cgroup的关系
一个进程限制内存和CPU
资源,就会绑定到CPU cgroup
和Memory cgroup
的节点上,CPU cgroup
节点和Memory cgroup
节点属于两个不同的层级(Hierarchy
)。进程和cgroup
节点是多对多的关系,因为一个进程涉及多个子系统,每个子系统可能属于不同的层次结构(Hierarchy
)。
如图:
上图P
代表进程,因为多个进程可能共享相同的资源,所以会抽象出一个CSS_SET
,每个进程会属于一个CSS_SET
链表中,同一个CSS_SET
下的进程都被其管理。一个CSS_SET
关联多个cgroup
节点,也就是关联多个子系统的资源控制,那么CSS_SET
和cgroup
节点就是多对多的关系。
参考下 CSS_SET
结构定义:
#ifdef CONFIG_CGROUPS /* Control Group info protected by css_set_lock */ struct css_set __rcu *cgroups; 关联的cgroup 节点 /* cg_list protected by css_set_lXock and tsk->alloc_lock */ struct list_head cg_list; // 关联所有的进程 #endif
2 为什么我们需要 cgroups
我们希望能够细粒度的控制资源,我们可以为一个系统的不同用户提供不同的资源使用量,比如一个学校的校园服务器,老师用户可以使用15%的资源,学生用户可以使用5%的资源。我们可以用 cgroups
机制做到。
3 crgoups 是如何实现的
3.1 cgroups 数据结构
- 每个进程都会指向一个
CSS_SET
数据结构。(上文进程和cgroups关系
已经提供过)
参考源码:
struct task_struct { //进程的数据结构 ... #ifdef CONFIG_CGROUPS /* Control Group info protected by css_set_lock */ struct css_set __rcu *cgroups; 关联的cgroup 节点 /* cg_list protected by css_set_lXock and tsk->alloc_lock */ struct list_head cg_list; // 关联所有的进程 #endif ... }
- 一个
CSS_SET
关联多个cgroup_subsys_state
对象,cgroup_subsys_state
指向一个cgroup
子系统。所以进程和cgroup
是不直接关联的,需要通过cgroup_subsys_state
对象确定属于哪个层级,属于哪个cgroup
节点。
参考下CSS_SET
源码: - 一个
cgroup hierarchy
(层次)其实是一个文件系统, 可以挂载在用户空间查看和操作。 - 我们可以查看、绑定任何一个
cgroup
节点下的所有进程Id(PID)
。
- 实现原理:
通过进程的fork和退出,从 CSS_SET attach 或者 detach 进程
。
3.2 cgroups文件系统
上面我们了解到进程和cgroup
的关系,那么在用户空间内的进程是如何使用cgroup
功能的呢?
cgroup
通过VFS
文件系统将功能暴露给用户,用户创建一些文件,写入一些参数即可使用,那么用户使用crgoup
功能会创建哪些文件?
文件如下:
tasks
文件: 列举绑定到某个cgroup
的所有进程ID(PID)
.cgroup.procs
文件: 列举一个cgroup
节点下的所有线程组ID.notify_on_release
flag
文件:填0
或1
,表示是否在cgroup
中最后一个task
退出时通知运行release agent
,默认情况下是0
,表示不运行。release_agent
文件: 指定release agent
执行脚本的文件路径(该文件在最顶层cgroup
目录中存在),在这个脚本通常用于自动化umount
无用的cgroup
- 每个子系统也会创建一些特有的文件。
3.3 什么是 VFS 文件系统
VFS
是一个内核抽象层,能够隐藏具体文件系统的实现细节,从而给用户态进程提供一套统一的 API
接口。VFS
使用了一种通用文件系统的设计,具体的文件系统只要实现了 VFS
的设计接口,就能够注册到 VFS
中,从而使内核可以读写这种文件系统。 这很像面向对象设计中的抽象类与子类之间的关系,抽象类负责对外接口的设计,子类负责具体的实现。其实,VFS
本身就是用c
语言实现的一套面向对象的接口。
3.4 clone_children标志是干什么的
这个标志只会影响 cpuset
子系统,如果这个标志在 cgroup
中开启,一个新的cpuset
子系统cgroup
节点 的配置会继承它的父级cgroup
节点配置。
4 如何使用 cgroups
我们创建一个cgroup
,使用到cpuset
这个cgroup
子系统,可以按照下面的步骤:
mount -t tmpfs cgroup_root /sys/fs/cgroup
mkdir /sys/fs/cgroup/cpuset
mount -t cgroup -ocpuset cpuset /sys/fs/cgroup/cpuset
- 通过创建和写入新的配置到
/sys/fs/cgroup/cpuset
虚拟文件系统,创建新的cgroup
- 启动一个父进程任务
- 得到进程
PID
,写入到/sys/fs/cgroup/cpuset
的tasks
文件中 fork
,exec
或者clone
父进程任务。
举个例子,我们可以创建一个cgroup
,命名为Charlie
,包含CPU
资源2
到3
核,memory
节点为1
,操作如下:
mount -t tmpfs cgroup_root /sys/fs/cgroup mkdir /sys/fs/cgroup/cpuset mount -t cgroup cpuset -ocpuset /sys/fs/cgroup/cpuset cd /sys/fs/cgroup/cpuset mkdir Charlie cd Charlie echo 2-3 > cpuset.cpus echo 1 > cpuset.mems echo $$ > tasks ## 查看cgroup信息 sh # sh是进入当前cgroup cat /proc/self/cgroup
默认只有
root
具有cgroup
的操作权限。
5 cgroup V2
版本有什么不一样
不同于v1
版本,cgroup v2
版本只有一个层级 Hierarchy
(层级)。
cgroup v2
的层级可以通过下面的命令进行挂载:
# mount -t cgroup2 none $MOUNT_POINT
cgroupv2
文件系统有一个根cgroup
,以0x63677270
数字来标识,所有支持v2
版本的子系统控制器会自动绑定到 v2
的唯一层级上并绑定到根cgroup
。没有使用cgroup v2
版本的进程,也可以绑定到v1
版本的层级上,保证了前后版本的兼容性。
在v2
版本中,因为只有一个层级,所有进程只绑定到cgroup
的叶子节点。
如图:
节点说明:
- 父节点开启的子系统控制器控制到儿子节点,比如
A节点
开启了memory controller
,那么C节点``cgroup
就可以控制进程的memory
。 - 叶子节点不能控制开启哪些子系统的
controller
,因为叶子节点关联进程Id
。所以非叶子节点不能控制进程的使用资源。
cgroup v2
的cgroup
目录下文件说明:
cgroup.procs
文件,用来关联进程Id
。这个文件在V1
版本使用列举线程组Id的。cgroup.controllers
文件(只读)和cgroup.subtree_control
文件是用来控制子cgroup
节点可以使用的 子系统控制器。tasks
文件用来关联进程信息,只有叶子节点有此文件。
5.1 为什么这么改造?
v1
版本为了灵活一个进程可能绑定多个层级(Hierarchy
),但是通常是每个层级对应一个子系统,多层级就显得没有必要。所以一个层级包含所有的子系统就比较简单容易管理。
5.2 线程模式
cgroup v2
版本支持线程模式,将 threaded
写入到cgroup.type
就会开启Thread
模式。当开始线程模式后,一个进程的所有线程属于同一个cgroup
,会采用Tree
结构进行管理。
6 总结
通过对 cgroup
的学习,大致了解Linux crgoup
的数据结构,v2
版本层级结构的优化和支持线程模式的功能。