`
izuoyan
  • 浏览: 9229497 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

一个资源管理系统的设计--解析linux的cgroup实现

阅读更多

将实体打散成不可再分的微粒,这样就可以使设计灵活化,最大限度的减少数据冗余。以CRM系统为例,虽然管理是基于一组控制元素而不是一个控制元素的,设计的时候还是以一个控制元素为基础。
linux的cgroup系统可谓是一个典范,它轻量地实现了诸如solaris的“容器”的概念,也许也是对linux本身“命名空间”的一种冲击。它是分层的,也可以说是树形的结构,一个“控制组”拥有一个ROOT,每一个ROOT控制一组元素,在这个ROOT当中可以建立很多的“组”(cgroup),每一个组还可以建立下级的组...。每新建一个cgroup必须确定一组它关心的cgroup_subsys,比如cpuset,memory,ns等,怎么确定呢?这是通过文件系统的mount实现的,当你执行:
mount -t cgroup cgroup -o cpuset memory my
的时候,你就建立一个ROOT,这个ROOT包含所有的进程,然后你可以在my目录中执行mkdir group1 group2,这样就建立了两个cgroup,实际上,当你mount的时候,系统就建立了一个虚拟的组group-root,如果你执行cat my/tasks,你会发现它包含了所有的进程,如果你执行cat my/group1/tasks,你会发现它是空的,因为还没有任何的进程被加入进去。现在执行echo 761>my/group1/tasks,那么pid为761的进程将被加入到group1,通过修改group1目录下的文件就可以对这个group1中当前tasks文件中包含的所有的进程进行控制了。这一切是如何实现的?实际上linux内核代码的cgroup子系统实现了类似数据库的结构,包括表结构和查询引擎,通过阅读代码可以看出,每一个进程task包含一个css_set类型的字段:
struct css_set {
struct kref ref;
struct hlist_node hlist;
struct list_head tasks; //解决冗余,包含所有使用这个set的进程
struct list_head cg_links; //解决冗余,包含所有参与管理这个set的cgroup。
struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];
};
既然它被一组subsys控制,为何不直接将每个subsys本身包含在task中呢?这是为了解决数据冗余的问题,因为其他的进程也可以受这些subsys控制。
有必要说一下上面的css_set结构中的cg_links字段,它包含了所有的参与管理这个set的cgroup,为何会这样呢?难道一个set不是由一个cgroup管理的吗?不是的,要知道管理是基于“一组”subsys的,而这一组并不一定是全部的编译进内核也就是内核支持的subsys。比如你分别用-o参数cpu,memory...mount了5个cgroup文件系统,那么一个进程关联的css_set就会由5个cgroup管理,每一个mount的ROOT会管理一个,这个ROOT会用文件系统的方式管理进程分别属于该ROOT的哪个cgroup。
下面看一下cgroup_subsys_state,这个结构可以看作静态cgroup_subsys结构的动态实例,静态的cgroup_subsys中包含了一些通用的方法,而动态的cgroup_subsys_state则仅仅是一个父类,具体的数据和额外的方法通过继承它来实现,比如:
struct mem_cgroup {
struct cgroup_subsys_state css;
struct res_counter res;
struct mem_cgroup_lru_info info;
int prev_priority; /* for recording reclaim priority */
struct mem_cgroup_stat stat;
};
任何时候,只要你得到了一个cgroup_subsys_state,并且根据其subsys_id确认它是关于memory的,那么就可以通过:
static inline struct cgroup_subsys_state *task_subsys_state(
struct task_struct *task, int subsys_id)
{
return rcu_dereference(task->cgroups->subsys[subsys_id]);
}

container_of(task_subsys_state(p, mem_cgroup_subsys_id), struct mem_cgroup, css);
来取得这个可被称为子类实例的mem_cgroup,接下来就可以操作它的数据了。终于可以看一下cgroup_subsys_state了:
struct cgroup_subsys_state {
struct cgroup *cgroup;
atomic_t refcnt;
unsigned long flags;
};
这个结构很简单,cgroup是它绑定的一个cgroup实例,从名称上也可以看出cgroup_subsys_state结构是动态的,它表示进程的subsys的state,由于它是被cgroup管理的,因此它也只能有一个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]; //这里仅包含对应ROOT相关的subsys
struct cgroupfs_root *root; //对应的那个ROOT
struct cgroup *top_cgroup;
struct list_head css_sets; //包含所有的它参与管理的css_set
struct list_head release_list;
}
前面提到过,进程要显式加入一个ROOT的cgroup,在加入的时候可能会为进程绑定一个新的css_set(必须保证css_set的subsys数组完全相同才能重用,如果之前没有这样的css_set建立,只好新建立一个),只要绑定了一个新的css_set,这个set就要加入到cgroup的css_sets链表中,最简单的可以在css_set中添加一个字段用于此目的,与此同时cgroup中也要增加一个list_head结构用来链接css_set的cg_links字段,这样做为何不好呢?它增加了两个数据结构的耦合性,同时也增加了数据的冗余性,因为一个cgroup的ROOT负责一组subsys,一个进程也是和一组subsys关联,因此只需要一个进程的一组subsys中被同一个ROOT管理的第一个加入到cgroup链表中就可以表示一个进程受到了这个cgroup的管理,比如进程p1加入group1,该group1的ROOT管理cpu和memory,那么其css_set的subsys数组中只需要cpu_id的这个subsys加入cgroup的css_sets链表就可以了,为了代码的简单,因此引入了一个中间结构,那就是cg_cgroup_link:
struct cg_cgroup_link {
struct list_head cgrp_link_list; //代表一个css_set加入到cgroup
struct list_head cg_link_list; //代表一个cgroup加入到css_set
struct css_set *cg; //指回css_set
... //后续的内核还要指回cgroup,这里的内核是2.6.26
};
在此必须说一下为何要有cg_cgroup_link这个结构体。如果在css_set中和cgroup中直接加入链表元素是解决不了多对多问题的,比如所有参与管理一个css_set的cgroup都将其链表元素加入 css_set的链表,反过来css_set的链表元素也应该加入一个cgroup的链表,代表它是该cgroup管理的css_set之一,现在问题来了,前面说过一个css_set可以属于很多ROOT,那么它到底加入哪个cgroup的链表呢?毕竟css_set和cgroup之间是如此单项一对多的关系耦合,因此解除耦合的办法就是设计一个中间结构,那就是cg_cgroup_link。
很多时候,很多人在网上写了一大堆关于分析“linux内核”的文章,很多文章都是仅仅分析代码流程,但是很少有文章能说明为何这么做(除非文章的作者着手提交一个补丁或者其它...)。其实linux内核就是一个数据库设计的教程,它不但展示了表结构,而且还有查询引擎,故而linux内核绝对是绝妙的哦!比如在input子系统中,input_handle这个结构体也是和cg_cgroup_link意义一样的,也是为了解决多对多的问题而设置的,还有一个明显的例子,那就是linux内核中的总线驱动架构。
由此看来,学习linux内核可以学到两大当今时髦的东西,一个就是OO,另一个就是数据库的设计,千万不要以为linux内核仅仅是底层的东西,搞应用的人不用学习,其实各个领域是相通的,我相信,当一个顶级的文学家听说了广义相对论的时候,他也一定会提出一些自己的看法的。我经常看历史著作,那些作者们看起来对任何领域都很感兴趣...
附:看一下cgroup的静态数据结构们吧
首先看一下一个静态的超类,那就是cgroup_subsys,它包含了一系列的接口,但是没有实现!
struct cgroup_subsys {
struct cgroup_subsys_state *(*create)(struct cgroup_subsys *ss,
struct cgroup *cgrp);
...//类似的接口
int subsys_id;
int active;
int disabled;
int early_init;
#define MAX_CGROUP_TYPE_NAMELEN 32
const char *name;
struct cgroupfs_root *root;
struct list_head sibling; //用于挂载一系列的state
void *private; //用于扩展
};
每一个挂载(mount)的cgroup文件系统都有一个cgroupfs_root实例:
struct cgroupfs_root {
struct super_block *sb;
unsigned long subsys_bits;
unsigned long actual_subsys_bits;
struct list_head subsys_list; //本ROOT关注的subsys
struct cgroup top_cgroup;
int number_of_cgroups;
struct list_head root_list;
unsigned long flags;
char release_agent_path[PATH_MAX];
};

分享到:
评论

相关推荐

    docker cgroup 资源监控的详解

    cgroup是Linux内核的一个特性,它允许系统管理员对一组进程的资源使用情况进行限制、记录和隔离。通过将一组进程分配到不同的cgroup中,系统管理员可以分别控制这些进程可以使用的资源,例如CPU、内存、磁盘I/O等。 ...

    [done]深⼊入解析Docker背后的Linux内核技术.pdf

    在解析Docker背后所依赖的Linux内核技术时,主要涉及到Namespace和CGroup这两种关键技术,它们分别负责实现资源隔离和资源限制。 Namespace是Linux内核中用于隔离系统资源的一种机制,使运行的进程能够获得系统的...

    用于管理Linux控制组(cgroup)的本地Rust库。-Rust开发

    现有技术的cgroup-它做很多事情(例如,在我不打算使用的子系统中创建cgroup,解析我不打算使用的控制文件)。 用法首先,将以下内容添加到您的Cargo.toml中:[dependencies] cgroups-fs =“ 1.0”接下来,在您的...

    Kubernetes 与 AI 相结合架构 落地解析(从 0 到 1).pdf

    OCI(Open Container Initiative)是由 Linux 基金会发起的一个项目,旨在定义容器格式和运行时的开放行业标准,确保容器跨平台的兼容性和一致性。 #### Kubernetes 架构详解 Kubernetes 架构主要由两部分组成:...

    任务管理器代码

    你可以通过阅读这些代码来学习如何与操作系统交互,以及如何设计和实现一个功能完备的任务管理器。在分析代码时,要注意代码结构、模块划分以及如何有效地使用系统资源。 总的来说,理解和编写任务管理器代码是一项...

    linux内核启动分析

    Bootloader作为系统启动的第一个程序,主要负责硬件初始化,如设置处理器、RAM以及其他外围设备(如UART、USB等)。随后,它会将Linux内核映像下载至RAM指定位置,并传递控制权给内核。在这个例子中,使用的硬件平台...

    container-info:返回有关当前容器的一些内存,CPU和cgroup信息

    3. **cgroup信息:内存**:控制组(cgroups)是Linux内核的一个功能,用于限制、记录和隔离进程组使用的物理资源,如CPU、内存等。在容器中,cgroups用来设定和监控资源限制。通过cgroups,我们可以设置每个容器的...

    004.Docker Engine安装配置指导1

    它可以让开发者打包应用及其依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器或 Windows 机器上,也可以实现虚拟化。本文将详细介绍 Docker Engine 在 Ubuntu Precise 12.04 (LTS)、Ubuntu Trusty ...

    BPF Performance Tools.zip

    总而言之,BPF Performance Tools是一个强大的性能分析框架,结合了Linux内核的先进特性和用户空间工具,使得系统管理员和开发者能够更深入地理解系统行为,定位并优化性能瓶颈,从而提升整体系统的效率和稳定性。...

    获取内存状态.rar

    同时,这也是一个很好的实践项目,可以帮助我们更好地理解操作系统是如何管理和报告内存资源的。 总结,获取内存状态是计算机系统管理中的重要一环,通过学习和分析"获取内存状态.cpp",我们可以深入理解操作系统的...

    DPDK编程指南

    - **网络报文缓冲区管理(librte_mbuf)**:管理着网络数据包的内存布局,每个报文都对应一个结构体,包含报文数据和其他相关信息。 - **定时器管理(librte_timer)**:用于管理周期性和非周期性的定时任务,支持高...

    CID_2021_在混合模式下构建基于硬件的性能监控基础设施1

    在构建高性能监控基础设施时,硬件性能计数器(PMU,...通过这些方法和技术,我们可以构建一个强大而灵活的性能监控基础设施,不仅能够及时发现并解决问题,还能在云计算环境中实现持续的性能优化和资源管理。

    长轮询查看服务器cpu的利用率

    在Linux系统中,可以使用`/proc/stat`文件或者`/sys/fs/cgroup/cpuacct/*`目录下的文件来获取CPU使用率;在Windows系统中,可以通过Performance Counter API来获取。 客户端(浏览器)端,可能使用JavaScript的...

    kubeadm init初始化流程分析

    Kubeadm是一个用于简化Kubernetes集群安装和管理的工具。在初始化过程中,kubeadm会进行一系列的环境检测来确保安装的成功率和稳定性。 1. **检查执行init命令的用户是否为root** - 目的是确保有足够的权限来完成...

Global site tag (gtag.js) - Google Analytics