首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Cgroup原理解释及部署实例(3)

Cgroup设计原理分析

CGroups的源代码较为清晰,我们可以从进程的角度出发来剖析cgroups相关数据结构之间的关系。在Linux中,管理进程的数据结构是task_struct,其中与cgroups有关的代码如清单8所示:

清单8.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结构,而css_set存储了与进程有关的cgroups信息。cg_list是一个嵌入的list_head结构,用于将连到同一个css_set的进程组织成一个链表。下面我们来看css_set的结构,代码如清单9所示:

清单9.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;

};

其中refcount是该css_set的引用数,因为一个css_set可以被多个进程公用,只要这些进程的cgroups信息相同,比如:在所有已创建的层级里面都在同一个cgroup里的进程。hlist是嵌入的hlist_node,用于把所有css_set组织成一个hash表,这样内核可以快速查找特定的css_set。tasks指向所有连到此css_set的进程连成的链表。cg_links指向一个由struct_cg_cgroup_link连成的链表。

Subsys是一个指针数组,存储一组指向cgroup_subsys_state的指针。一个cgroup_subsys_state就是进程与一个特定子系统相关的信息。通过这个指针数组,进程就可以获得相应的cgroups控制信息了。

cgroup_subsys_state结构如清单10所示:

清单10.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结构如清单11所示:

清单11.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所在的层级对应的结构体。这样一来,之前谈到的几个cgroups概念就全部联系起来了。

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

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

下面分析一个css_set和cgroup之间的关系,cg_cgroup_link的结构如清单12所示:

清单12.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连入到cgrouo->css_set指向的链表,cgrp则指向此cg_cgroup_link相关的cgroup。

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

cgroup和css_set是一个多对多的关系,必须添加一个中间结构来将两者联系起来,这就是cg_cgroup_link的作用。cg_cgroup_link中的cgrp和cg就是此结构提的联合主键,而cgrp_link_list和cg_link_list分别连入到cgroup和css_set相应的链表,使得能从cgroup或css_set都可以进行遍历查询。

那为什么cgroup和css_set是多对多的关系呢?

一个进程对应一个css_set,一个css_set存储了一组进程(有可能被多个进程共享,所以是一组)跟各个子系统相关的信息,但是这些信息由可能不是从一个cgroup那里获得的,因为一个进程可以同时属于几个cgroup,只要这些cgroup不在同一个层级。举个例子:我们创建一个层级A,A上面附加了cpu和memory两个子系统,进程B属于A的根cgroup;然后我们再创建一个层级C,C上面附加了ns和blkio两个子系统,进程B同样属于C的根cgroup;那么进程B对应的cpu和memory的信息是从A的根cgroup获得的,ns和blkio信息则是从C的根cgroup获得的。因此,一个css_set存储的cgroup_subsys_state可以对应多个cgroup。另一方面,cgroup也存储了一组cgroup_subsys_state,这一组cgroup_subsys_state则是cgroup从所在的层级附加的子系统获得的。一个cgroup中可以有多个进程,而这些进程的css_set不一定都相同,因为有些进程可能还加入了其他cgroup。但是同一个cgroup中的进程与该cgroup关联的cgroup_subsys_state都受到该cgroup的管理(cgroups中进程控制是以cgroup为单位的)的,所以一个cgroup也可以对应多个css_set。

从前面的分析,我们可以看出从task到cgroup是很容易定位的,但是从cgroup获取此cgroup的所有的task就必须通过这个结构了。每个进程都回指向一个css_set,而与这个css_set关联的所有进程都会链入到css_set->tasks链表,而cgroup又通过一个中间结构cg_cgroup_link来寻找所有与之关联的所有css_set,从而可以得到与cgroup关联的所有进程。最后,我们看一下层级和子系统对应的结构体。层级对应的结构体是

cgroupfs_root如清单13所示:

清单13.cgroupfs_root代码

struct cgroupfs_root {

struct super_block *sb;

unsigned long subsys_bits;

int hierarchy_id;

unsigned long actual_subsys_bits;

struct list_head subsys_list;

struct cgroup top_cgroup;

int number_of_cgroups;

struct list_head root_list;

unsigned long flags;

char release_agent_path[PATH_MAX];

char name[MAX_CGROUP_ROOT_NAMELEN];

};

sb指向该层级关联的文件系统数据块。

subsys_bits和actual_subsys_bits分别指向将要附加到层级的子系统和现在实际附加到层级的子系统,在子系统附加到层级时使用。hierarchy_id是该层级唯一的id。top_cgroup指向该层级的根cgroup。number_of_cgroups记录该层级cgroup的个数。root_list是一个嵌入的list_head,用于将系统所有的层级连成链表。子系统对应的结构体是cgroup_subsys,代码如清单14所示。

清单14. cgroup_subsys代码

struct cgroup_subsys {

struct cgroup_subsys_state *(*create)(struct cgroup_subsys *ss, struct cgroup *cgrp);

int (*pre_destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp);

void (*destroy)(struct cgroup_subsys *ss, struct cgroup *cgrp);

int (*can_attach)(struct cgroup_subsys *ss, struct cgroup *cgrp, struct task_struct *tsk, bool threadgroup);

void (*cancel_attach)(struct cgroup_subsys *ss, struct cgroup *cgrp, struct task_struct *tsk, bool threadgroup);

void (*attach)(struct cgroup_subsys *ss, struct cgroup *cgrp, struct cgroup *old_cgrp, struct task_struct *tsk, bool threadgroup);

void (*fork)(struct cgroup_subsys *ss, struct task_struct *task);

void (*exit)(struct cgroup_subsys *ss, struct task_struct *task);

int (*populate)(struct cgroup_subsys *ss, struct cgroup *cgrp);

void (*post_clone)(struct cgroup_subsys *ss, struct cgroup *cgrp);

void (*bind)(struct cgroup_subsys *ss, struct cgroup *root);

int subsys_id;

int active;

int disabled;

int early_init;

bool use_id;

#define MAX_CGROUP_TYPE_NAMELEN 32

const char *name;

struct mutex hierarchy_mutex;

struct lock_class_key subsys_key;

struct cgroupfs_root *root;

struct list_head sibling;

struct idr idr;

spinlock_t id_lock;

struct module *module;

};

cgroup_subsys定义了一组操作,让各个子系统根据各自的需要去实现。这个相当于C++中抽象基类,然后各个特定的子系统对应cgroup_subsys则是实现了相应操作的子类。类似的思想还被用在了cgroup_subsys_state中,cgroup_subsys_state并未定义控制信息,而只是定义了各个子系统都需要的共同信息,比如该cgroup_subsys_state从属的cgroup。然后各个子系统再根据各自的需要去定义自己的进程控制信息结构体,最后在各自的结构体中将cgroup_subsys_state包含进去,这样通过Linux内核的container_of等宏就可以通过cgroup_subsys_state来获取相应的结构体。

从基本层次顺序定义上来看, 由task_struct、css_set、cgroup_subsys_state、cgroup、cg_cgroup_link、cgroupfs_root、cgroup_subsys等结构体组成的CGroup可以基本从进程级别反应之间的响应关系。后续文章会针对文件系统、各子系统做进一步的分析。

结束语

就象大多数开源技术一样,CGroup不是全新创造的,它将进程管理从cpuset中剥离出来。通过物理限制的方式为进程间资源控制提供了简单的实现方式,为Linux Container技术、虚拟化技术的发展奠定了技术基础,本文的目标是让初学者可以通过自己动手的方式简单地理解技术,将起步门槛放低。

发个小广告!!!走过路过,不要错过

注:本公众号与当当店铺并无从属关系,仅为大家提供一个便捷购物地址。若有所冲突,纯属巧合,立删。

麦克叔叔每晚十点说

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20171221B0X3MF00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券