[Android Cgroup层次关系] 一、Cgroup初始化

        相对于PC、服务器而言,移动终端的资源更为紧缺。一方面,受限于电池技术,手机的电池容量有限;另一方面,为了追求更好的用户体验,手机都设计得较为轻薄,这种结构使得手机的散热能力较低。严格的发热和续航要求,时刻制约着手机SOC运算能力的发挥。在这种资源有限的背景下,资源的分配就显得尤为重要。Android利用Cgroup机制对进程分组控制,为参与用户交互的进程分配更多资源,限制后台程序过多使用资源,以保障用户体验。

        系统中有各种类型的资源需要分组控制,比如CPU、块IO、Memory等,每一种类型称为Cgroup子系统。内核将Cgroup子系统抽象为结构体struct cgroup_subsys,包括id、name和操作接口等。全局变量struct cgroup_subsys *cgroup_subsys[]是所有子系统的集合,在cgroup_init_early()中初始化各个分组的id和name。

struct cgroup_subsys {
    //子系统的各种行为抽象为接口
	struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css);
	int (*css_online)(struct cgroup_subsys_state *css);
	void (*css_offline)(struct cgroup_subsys_state *css);
	void (*css_released)(struct cgroup_subsys_state *css);
	void (*css_free)(struct cgroup_subsys_state *css);
	void (*css_reset)(struct cgroup_subsys_state *css);
	int (*can_attach)(struct cgroup_taskset *tset);
    ..................................................................................
	bool early_init:1;         //是否需要在cgroup_init_early阶段初始化,默认为true
	int id;                    //每个子系统都有唯一的id
	const char *name;          //子系统名称
	..................................................................................
	struct cgroup_root *root;  //子系统对应的cgroup_root
	struct list_head cfts;     //子系统属性(cftype)集合
    ..................................................................................
};

        一个子系统一般对应多个分组,例如cpuset子系统就有top-app、foreground等分组。一个分组可以对应多个子系统,在Android上一个分组对应一个子系统,为了简化起见,后面都用一个分组对应一个子系统为例。分组管控,本质上就是每个分组拥有独立的子系统状态(参数),系统根据分组中进程对应的子系统状态(参数)分配资源。子系统状态用结构体struct cgroup_subsys_state表示,该结构体并不直接表示子系统状态,而是嵌入到子系统状态的结构体中。例如cpuset的状态struct cpuset就包含cgroup_subsys_state,使用时通过cgroup_subsys_state找到包含它的struct cpuset。

struct cpuset {
	struct cgroup_subsys_state css;
    .........................................................
}

struct cgroup_subsys_state {
	struct cgroup *cgroup;          //属于哪个分组
	struct cgroup_subsys *ss;       //表示哪个子系统的状态
    //下面三个参数用于构建状态层次关系
	struct cgroup_subsys_state *parent;
	struct list_head sibling;
	struct list_head children;
    .........................................................
};

         结构体struct cgroup用于表示分组,成员subtree_ss_mask表明该分组对应哪些子系统,subsys[]存放属于该分组的子系统状态,root指向cgroup所属层级的cgroup_root。cgroup_root是在mount子系统时创建的,表示子系统的一个层级。cgroup_root中包含一个cgroup,是该层级的默认分组。

struct cgroup {
    //用于链接cgroup层次关系
	struct cgroup_subsys_state self;
	struct kernfs_node *kn;		    /* kernfs 中对应的节点 */
	u16 subtree_ss_mask;  //对应哪些子系统
	//子系统状态
	struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT];
	struct cgroup_root *root; //所示层级
    ...............................................................
};

struct cgroup_root {
	struct kernfs_root *kf_root;
	unsigned int subsys_mask;        //子系统掩码
	...............................................................
	struct cgroup cgrp;              //默认分组 
	struct list_head root_list;
    ...............................................................
};

        cgroup的初始化是从cgroup_init_early()开始的,该函数中初始化全局变量struct cgroup_subsys *cgroup_subsys[]中所有子系统的id、name等。

int __init cgroup_init_early(void)
{
	static struct cgroup_sb_opts __initdata opts;
	struct cgroup_subsys *ss;
	int i;
    //初始化cgrp_dfl_root
	init_cgroup_root(&cgrp_dfl_root, &opts);
	cgrp_dfl_root.cgrp.self.flags |= CSS_NO_REF;

	RCU_INIT_POINTER(init_task.cgroups, &init_css_set);

	for_each_subsys(ss, i) {
        ........................................................................
		ss->id = i;                          //初始化id
		ss->name = cgroup_subsys_name[i];    //初始化name
		if (!ss->legacy_name)
			ss->legacy_name = cgroup_subsys_name[i];

		if (ss->early_init)                  //early_init默认为true
			cgroup_init_subsys(ss, true);
	}
	return 0;
}

 全局变量struct cgroup_root cgrp_dfl_root也是在cgroup_init_early()阶

段初始化的。struct cgroup代表一个控制分组,struct cgroup_root在Cgroup层级中代表子系统控制分组的根,是一个特殊的控制分组,从struct cgroup_root的定义可以看出,它是在struct cgroup的基础上进行了扩展。 cgroup_root的初始化在init_cgroup_root()中完成。

static void init_cgroup_root(struct cgroup_root *root, struct cgroup_sb_opts *opts)
{
	struct cgroup *cgrp = &root->cgrp;

	INIT_LIST_HEAD(&root->root_list);
	atomic_set(&root->nr_cgrps, 1);
	cgrp->root = root;
	init_cgroup_housekeeping(cgrp);
	idr_init(&root->cgroup_idr);

	root->flags = opts->flags;
	if (opts->release_agent)
		strcpy(root->release_agent_path, opts->release_agent);
	if (opts->name)
		strcpy(root->name, opts->name);
	if (opts->cpuset_clone_children)
		set_bit(CGRP_CPUSET_CLONE_CHILDREN, &root->cgrp.flags);
}

static void init_cgroup_housekeeping(struct cgroup *cgrp)
{
	struct cgroup_subsys *ss;
	int ssid;

	INIT_LIST_HEAD(&cgrp->self.sibling);
	INIT_LIST_HEAD(&cgrp->self.children);
	INIT_LIST_HEAD(&cgrp->cset_links);
	INIT_LIST_HEAD(&cgrp->pidlists);
	mutex_init(&cgrp->pidlist_mutex);
	cgrp->self.cgroup = cgrp;
	cgrp->self.flags |= CSS_ONLINE;

	for_each_subsys(ss, ssid)
		INIT_LIST_HEAD(&cgrp->e_csets[ssid]);

	init_waitqueue_head(&cgrp->offline_waitq);
	INIT_WORK(&cgrp->release_agent_work, cgroup_release_agent);
}

        cgrp_dfl_root初始化后的结构体关系如下图所示,cgroup_root包含一个cgroup,该cgroup的成员*root指向croup_rootcgroup包含结构体cgroup_subsys_state,该cgroup_subsys_state的成员*cgroup指向包含它的cgroup。

         子系统的early_init默认情况下是true(bool early_init:1),无特殊声明的子系统在cgroup_init_early()中都会调用cgroup_init_subsys()。cgroup_init_subsys()中将子系统的struct cgroup_root *root成员指向 cgrp_dfl_root,然后调用子系统css_alloc接口创建struct cgroup_subsys_state。此时css_alloc接口传入的参数为空,返回的是子系统默认状态。

static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)
{
	struct cgroup_subsys_state *css;

	pr_debug("Initializing cgroup subsys %s\n", ss->name);

	mutex_lock(&cgroup_mutex);

	idr_init(&ss->css_idr);
	INIT_LIST_HEAD(&ss->cfts);

	/* Create the root cgroup state for this subsystem */
	ss->root = &cgrp_dfl_root;
	css = ss->css_alloc(cgroup_css(&cgrp_dfl_root.cgrp, ss));
	/* We don't handle early failures gracefully */
	BUG_ON(IS_ERR(css));
	init_and_link_css(css, ss, &cgrp_dfl_root.cgrp);
    ...........................................................
	BUG_ON(online_css(css));

	mutex_unlock(&cgroup_mutex);
}

        init_and_link_css()中struct cgroup_subsys_state的成员struct cgroup *cgroup指向cgroup,成员struct cgroup_subsys *ss指向对应子系统struct cgroup_subsys,建立起cgroup与cgroup_subsys之间的联系。

static void init_and_link_css(struct cgroup_subsys_state *css,
			      struct cgroup_subsys *ss, struct cgroup *cgrp)
{
	lockdep_assert_held(&cgroup_mutex);

	cgroup_get(cgrp);

	memset(css, 0, sizeof(*css));
	css->cgroup = cgrp;
	css->ss = ss;
	css->id = -1;
	INIT_LIST_HEAD(&css->sibling);
	INIT_LIST_HEAD(&css->children);
	css->serial_nr = css_serial_nr_next++;
	atomic_set(&css->online_cnt, 0);

	if (cgroup_parent(cgrp)) {
		css->parent = cgroup_css(cgroup_parent(cgrp), ss);
		css_get(css->parent);
	}

	BUG_ON(cgroup_css(cgrp, ss));
}

        最会调用online_css()将struct cgroup_subsys_state指针存放在所属cgroup的subsys[]对应位置。cgroup_init_subsys()完成后,结构体关系如下图所示。

static int online_css(struct cgroup_subsys_state *css)
{
	struct cgroup_subsys *ss = css->ss;
	int ret = 0;

	lockdep_assert_held(&cgroup_mutex);

	if (ss->css_online)
		ret = ss->css_online(css);
	if (!ret) {
		css->flags |= CSS_ONLINE;
		rcu_assign_pointer(css->cgroup->subsys[ss->id], css);

		atomic_inc(&css->online_cnt);
		if (css->parent)
			atomic_inc(&css->parent->online_cnt);
	}
	return ret;
}

         进入某个Cgroup子系统目录(例如/dev/blkio/)下会看到许多控制文件节点,这些文件节点是根据struct cftype来创建的,子系统包含的所有struct cftype都在内核链表struct list_head cfts中。以cpuset子系统为例,定义的时候会初始化legacy_cftypes成员,在cgroup_init()中以成员legacy_cftypes为参数初始化子系统的list_head cfts。

static struct cftype files[] = {
	{
		.name = "cpus",
		.seq_show = cpuset_common_seq_show,
		.write = cpuset_write_resmask,
		.max_write_len = (100U + 6 * NR_CPUS),
		.private = FILE_CPULIST,
	},

	{
		.name = "mems",
		.seq_show = cpuset_common_seq_show,
		.write = cpuset_write_resmask,
		.max_write_len = (100U + 6 * MAX_NUMNODES),
		.private = FILE_MEMLIST,
	},
    ...................................................
};

struct cgroup_subsys cpuset_cgrp_subsys = {
	.css_alloc	= cpuset_css_alloc,
	.css_online	= cpuset_css_online,
	.css_offline	= cpuset_css_offline,
	.css_free	= cpuset_css_free,
	.can_attach	= cpuset_can_attach,
	.cancel_attach	= cpuset_cancel_attach,
	.attach		= cpuset_attach,
	.post_attach	= cpuset_post_attach,
	.bind		= cpuset_bind,
	.fork		= cpuset_fork,
	.legacy_cftypes	= files,
	.early_init	= true,
};

int __init cgroup_init(void)
{
	struct cgroup_subsys *ss;
	int ssid;
    ....................................................................
	for_each_subsys(ss, ssid) {
                 ................................................................
		if (ss->dfl_cftypes == ss->legacy_cftypes) {
			WARN_ON(cgroup_add_cftypes(ss, ss->dfl_cftypes));
		} else {
			WARN_ON(cgroup_add_dfl_cftypes(ss, ss->dfl_cftypes));
			WARN_ON(cgroup_add_legacy_cftypes(ss, ss->legacy_cftypes));
		}
        ................................................................
	}
    ....................................................................
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值