cgroup
可能很多人都不了解,但提起 Docker
估计每个后端程序员都了解过。是的,Docker
已经成为程序员必须掌握的技术之一了。Docker
主要解决了传统虚拟机启动慢、占用大量资源的缺点。
当然,本文的重点并不是 Docker
,而是 Docker
的底层支撑技术 cgroup
。可以这样说,没有 cgroup
就没有 Docker
。
cgroup
的全称为 control group
,中文翻译为 控制组
。主要用于控制进程组对某种资源的使用,这些资源包括但不限于:内存
、CPU
、I/O
和 网络
等。
如下图所示,使用 cgroup
来限制进程组对内存的使用:
(图1)
在上图中,我们创建了 2 个 cgroup
(每个 cgroup 有 4 个进程),并且限制它们各自最多只能使用 2GB 的内存。如果使用超过 2GB 的内存,那么将会触发 OOM(Out Of Memory)
错误。
cgroup
通过把进程划分成控制组(一个控制组包含一个或多个进程),并且可以对控制组进行资源使用的控制,也就是说 cgroup
作用对象是控制组。
cgroup
提供了将进程组织成控制组的能力,然后通过使用 资源控制子系统(cgroup_subsys)
来对控制组进行资源使用的控制,cgroup
支持的 资源控制子系统
有以下几种:
cpu子系统
:限制 CPU 的使用。memory子系统
:限制内存使用。cpuset子系统
:可以为进程组分配单独的 CPU 或者内存节点。cpuacct子系统
:统计CPU group的使用情况。blkio子系统
:限制I/O,一般用于磁盘。devices子系统
:限制进程使用的设备。freezer子系统
:可以挂起和恢复进程组。net_cls子系统
:可以标记进程组的网络数据包,使用 tc 模块(traffic control)对数据包进行控制。也就是说,cgroup
通过把进程组织成 控制组
,然后通过 资源控制子系统
来对 控制组
进行资源使用的限制,所以 cgroup
的分析可以分成两部分:cgroup框架
和 资源控制子系统
。
cgroup
的设计还是比较复杂的,主要是因为 cgroup
涉及多种资源的控制,并且 cgroup
通过虚拟文件系统来组织进程控制组,所以导致 cgroup
的实现变得复杂难懂。
cgroup
的概念和使用可以参考这篇文章:《[cgroup介绍] 》。
为了不会让大家陷入枯燥的概念和源码之中,本文主要通过以设计者的角度来分析 cgroup
的设计与实现。
OK,Let's go!
如果让你来设计一个限制进程组对内存使用的方案,你会怎么设计呢?
最简单的方法就是,创建一个内存使用的计数器,然后将进程组中所有的进程都指向这个计数器。当进程组的进程申请内存时,就增加计数器的值,如果计数器超过限制就触发错误。如下图所示:
(图2)
上图中计数器的 limit
字段表示限制进程中使用的最大内存数,而 count
字段表示当前进程使用的内存数。每当进程组中的进程申请内存时,都需要增加计数器的 count
字段,并且比较 count
是否已经超出 limit
的限制。
数据结构可以这样设计:
(图3)
我们通过链表来将进程组织成进程组,并且在计数器中增加一个 task_group
字段,让其指向进程组。当进程组中的进程申请内存时,可以通过指针来找到对应的计数器,并且增加计数器的 count
字段。
就这样,我们设计了一个简单的 cgroup
功能。如果系统只有内存这种资源的话,的确可以这样设计。但是系统除了内存,还有CPU、硬盘和网络这些资源,所以 Linux 创建了一种比较通用的方式来组织进程组。
有了上面的雏形,cgroup 的很多概念就比较容易理解了,下面主要介绍一下 控制组
这个概念。
控制组
说白了就是一组进程(进程组),cgroup 就是用来限制 控制组
的资源使用。为了能够方便地向一个 控制组
添加或者移除进程(在命令行也能操作),内核使用了 虚拟文件系统
来进行管理 控制组
。
我们可以把一个 控制组
当成是一个目录,由于目录有层级关系,所以 控制组
也有层级关系,如下图所示:
(图4)
如上图所示,控制组
是以目录树来组织的,每一个目录代表一个 控制组
。在内核中,一个由 控制组
组成的目录树被称为 层级(hierarchy)
。
每个控制组目录中,都有一个名为 tasks
的文件,用于保存当前 控制组
包含的进程列表。如果我们想向某个 控制组
添加一个进程时,可以把进程的 PID
写入到 tasks
文件中。例如:
$ cd /sys/fs/cgroup/memory/cgrp1 # 进入控制组cgrp1
$ echo 1029 > tasks # 将PID为1029的进程添加到cgrp1控制组中
我们也可以通过读取 tasks
文件来查看某个 控制组
中的进程列表,例如:
$ cat tasks
1
2
3
5
6
7
...
在内核中,控制组使用 cgroup
结构来表示,其定义如下:
struct cgroup {
...
// 下面3个字段把控制组连接成一个树结构
struct list_head sibling; // 兄弟节点
struct list_head children; // 子节点
struct cgroup *parent; // 父节点
struct dentry *dentry; // 当前控制组对应的目录对象
// 当前控制组关联的子系统资源统计对象
struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
...
};
内核通过 cgroup
结构的 sibling
、children
和 parent
这3个字段来将 控制组
组织成一棵树状结构。如下图所示:
(图5)
另外,cgroup
结构的 subsys
字段表示当前控制组关联的子系统状态对象,下面介绍 资源控制子系统
时将会详细介绍。
在 Linux 内核中,可以存在多个 层级(控制组树)
,每个层级可以关联一个或多个 资源控制子系统
,但同一个 资源控制子系统
不能关联到多个层级中(也就是说,同一种 资源控制子系统
只能关联到一个层级)。如下图所示:
(图6)
在内核中,层级
的根结点使用 cgroupfs_root
结构来表示, 我们来看看其定义:
struct cgroupfs_root {
struct super_block *sb; // 挂载点超级块对象(虚拟文件系统使用)
unsigned long subsys_bits; // 当前层级绑定的资源子系统位图(1表示已经绑定到当前层级)
...
struct list_head subsys_list; // 绑定到当前层级的资源子系统列表
struct cgroup top_cgroup; // 当前层级的根控制组
int number_of_cgroups; // 当前层级拥有的控制组数量
...
};
在 Linux 内核中,有个名为 rootnode
的根层级,在系统启动后,由内核自动创建并且初始化的层级。系统启动后,所有的资源控制子系统都关联到此层级。rootnode
的定义如下:
// 定义在文件 ./kernel/cgroup.c 中
static struct cgroupfs_root rootnode;
如果用户想把资源控制子系统关联到其他层级,那么可以使用 mount
命令来进行挂载,如下命令所示:
$ mount -t cgroup -o memory memory /sys/fs/cgroup/memory
上面的命令用于将内存子系统重新关联到 /sys/fs/cgroup/memory
这个层级。
我们继续来介绍 资源控制子系统 (下面简称子系统)
这个重要的概念。
在 设计一个简单的 cgroup 例子中,主要以内存资源作为分析对象。但我们知道,计算机不单止只有内存资源,还有譬如 CPU、硬盘和网络等资源。所以,cgroup 不单止要控制内存资源的使用,还要控制 CPU、硬盘和网络等资源的使用。如下图所示:
(图7)
在上面的实例中,我们使用一个计数器来统计进程组对内存资源的使用情况,每个 控制组
都需要一个这样的计数器来统计和限制进程组对内存资源的使用。
在 Linux 内核中也有类似的 “计数器“,使用 cgroup_subsys_state
结构来表示(我们称它为 资源统计对象
),其定义如下:
struct cgroup_subsys_state {
struct cgroup *cgroup; // 指向控制组对象
atomic_t refcnt; // 引用计数器
unsigned long flags; // 标志位
};
cgroup_subsys_state
结构看起来非常简单,这只是表面现象。内核为了将所有的 资源统计对象
抽象化(也就是都能用 cgroup_subsys_state
指针来指向所有类型的 资源统计对象
),才定义出这个通用的部分,实际上的 资源统计对象
是比较复杂的。
例如内存的 资源统计对象
定义如下:
struct mem_cgroup {
// 资源统计对象通用部分
struct cgroup_subsys_state css;
// 资源统计对象私有部分
struct res_counter res; // 用于统计进程组的内存使用情况
struct mem_cgroup_lru_info info;
int prev_priority;
struct mem_cgroup_stat stat;
};
mem_cgroup
结构与 cgroup_subsys_state
结构的关系如下图所示:
(图8)
资源统计对象
必须与 控制组
绑定,才能实现限制 控制组
对资源的使用。前面我们了解到 cgroup
结构中有个名为 subsys
的字段,如下代码所示:
struct cgroup {
...
// 当前控制组关联的资源统计对象
struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
...
};
可以看出,subsys
字段是一个 cgroup_subsys_state
结构的数组,数组的大小为系统支持的 资源控制子系统
数(也就是说,数组上的每个槽位对应着一个子系统资统计对象)。如下图所示:
(图9)
在 Linux 内核中,一个进程可以属于多个 控制组
,而每个 控制组
又关联着一个或多个 资源统计对象
。所以,一个进程所关联的 资源统计对象
是其所在 控制组
关联的 资源统计对象
的集合。这句话有点难懂,我们用一幅图来说明:
(图10)
如上图所示:
进程A
属于控制组 /sys/fs/cgroup/memory/cgrp1/cgrp3
和控制组 /sys/fs/cgroup/cpu/cgrp2/cgrp3
,所以 进程A
就关联了 mem_group A
和 task_group A
这两个资源统计对象。进程B
属于控制组 /sys/fs/cgroup/memory/cgrp1/cgrp4
和控制组 /sys/fs/cgroup/cpu/cgrp2/cgrp3
,所以 进程B
就关联了 mem_group B
和 task_group A
这两个资源统计对象。进程通过 css_set
结构来收集不同控制组的 资源统计对象
,其定义如下:
struct css_set {
...
// 用于收集不同控制组的资源统计对象
struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
};
在 进程描述符结构(task_struct)
中有个指向 css_set
结构的指针,如下所示:
struct task_struct {
...
struct css_set *cgroups;
...
};
所以,当把一个进程添加到一个 控制组
时,将会把 控制组
关联的 资源统计对象
添加到进程的 cgroups
字段中,从而使进程受到这些 资源统计对象
的限制,结合图10就比较容易理解了。
另外,资源子系统必须关联到某个层级才能起到限制 控制组
使用的目的。每种资源子系统都由一个名为 cgroup_subsys
的结构来描述,其定义如下:
struct cgroup_subsys {
struct cgroup_subsys_state *(*create)(struct cgroup_subsys *ss,
struct cgroup *cgrp);
...
void (*attach)(struct cgroup_subsys *ss, struct cgroup *cgrp,
struct cgroup *old_cgrp, struct task_struct *tsk);
void (*fork)(struct cgroup_subsys *ss, struct task_struct *task);
void (*exit)(struct cgroup_subsys *ss, struct task_struct *task);
...
int subsys_id;
int active;
int disabled;
int early_init;
const char *name; // 子系统名字
struct cgroupfs_root *root; // 关联的层级根节点
struct list_head sibling;
void *private;
};
从 cgroup_subsys
结构的定义可以看出,其主要定义了一些方法和关联的层级。比如:create
方法主要用于当新建一个 控制组
时,创建一个新的 资源统计对象
与其关联;而 root
字段指向关联的层级根节点。
如内存子系统的定义如下:
// 定义在文件:./mm/memcontrol.c
struct cgroup_subsys mem_cgroup_subsys = {
.name = "memory",
.subsys_id = mem_cgroup_subsys_id,
.create = mem_cgroup_create,
.pre_destroy = mem_cgroup_pre_destroy,
.destroy = mem_cgroup_destroy,
.populate = mem_cgroup_populate,
.attach = mem_cgroup_move_task,
.early_init = 0,
};
本文主要分析了 cgroup 的设计与源码实现,不过聪明的读者可能发现本文并没有分析 cgroup 的逻辑代码。
是的,本文并没有分析具体的逻辑代码实现。不过按照本文的设计分析,相信读者能够很容易看到 cgroup 的逻辑代码实现,有兴趣的读者可以自行阅读源代码。
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/PHQlN4-CWMYbibM7r82PIg
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。