[知其然不知其所以然-15] cgroup概述
来源:互联网 发布:复杂网络中的幂律分布 编辑:程序博客网 时间:2024/05/01 04:04
cgroup提出的背景是对进程分组,然后以组为单位占用资源并调度。
学习一个内核功能的捷径是通过sysfs文件。我们这里先不管cgroup内部复杂的数据结构,
直接给出cgroup重要的几个操作。
首先cgroup是被mount后才能使用的:
tmpfs on /sys/fs/cgroup type tmpfs (rw,mode=755)cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd) time)cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)cgroup on /sys/fs/cgroup/net_cls type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls)cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children)cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb,release_agent=/run/cgmanager/agents/cgm-release-agent.hugetlb)cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event,release_agent=/run/cgmanager/agents/cgm-release-agent.perf_event)主要关注cpuset:
mount一旦完成,第一个cpuset(也就是根cgroup)默认管理所有cpu,
chenyu@Surface-Pro-3:/$ cat /sys/fs/cgroup/cpuset/cpuset.cpus0-3
我们看到这个条目是以cpuset.开头的,说明这个条目是cpuset特有的字段。
在cpuset下还有一些通用字段,不管mount的是cpuset,还是memory等子系统,
都会有的字段,比如进程:
chenyu@Surface-Pro-3:/$ cat /sys/fs/cgroup/cpuset/tasks | wc 158 158 572表示cpuset下的根cgroup默认管理所有的task。
可以在cpuset下通过mkdir创建目录,例如创建一个名字为test的目录:
root@Surface-Pro-3:/# mkdir /sys/fs/cgroup/cpuset/testroot@Surface-Pro-3:/# ls /sys/fs/cgroup/cpuset/test/cgroup.clone_children cpuset.memory_pressurecgroup.procs cpuset.memory_spread_pagecpuset.cpu_exclusive cpuset.memory_spread_slabcpuset.cpus cpuset.memscpuset.effective_cpus cpuset.sched_load_balancecpuset.effective_mems cpuset.sched_relax_domain_levelcpuset.mem_exclusive notify_on_releasecpuset.mem_hardwall taskscpuset.memory_migrate在创建test目录后,test目录下会自动创建一系列的条目。
通过对这些条目的修改,可以指定该cgroup(即test目录)可以管理哪些cpu。
下面分别来看mount,mkdir时发生了什么。
先看mount:
static struct dentry *cgroup_mount(struct file_system_type *fs_type, int flags, const char *unused_dev_name, void *data){//get user paramsparse_cgroupfs_options(data, &opts);for_each_root(root) {if (opts.subsys_mask != root->subsys_mask)if (!name_match)continue;ret = -EBUSY;goto out_unlock;} root = kzalloc(sizeof(*root), GFP_KERNEL); root->cgrp->root = root; cgroup_setup_root(root, opts.subsys_mask);}cgroup_mount先是根据mount的参数获得用户的参数,比如cpuset的参数是:
(rw,nosuid,nodev,noexec,relatime,cpuset,clone_children)
则parse_cgroupfs_options会以逗号为分割,获取各个用户参数。
这些参数中最重要的就是subsys_mask,也就是cpuset,表示将要生成的
新cgroup会启用cpuset这个subsystem。
接下来的for_each_root循环是完备性检查,主要就是看用户提供的subsys_mask(cpuset)
是否已经被别的cgroup目录挂载了,如果是的话就返回-EBUSY退出。否则,通过了检查,
就可以新生成一个cgroup根了,这个根比较特殊,是一个包含了cgroup的结构体,因为需要
保存整个树的其他信息,因此被封装成了cgroup_root结构体,接下来对这个cgroup根做进一步的
初始化,调用cgroup_setup_root(root, opts.subsys_mask)的意思是,把系统中对应opts.subsys_mask
的所有subsys系统,都attach到root指向的cgroup根上。
subsys系统,也就是cpuset,memory,devices等,在我们的例子里是cpuset,然后cpuset的初始化
要提前于cgroup的初始化,其实就是一个静态全局变量:
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,.bind= cpuset_bind,.legacy_cftypes= files,.early_init= 1,};
注意,任意一个subsys,都只能和一个
cgroup 根/tree结合,所以如果subsys_mask对应的subsys已经被绑定到其他的cgroup_root了,那么需要先解除
绑定,再绑定到本次指定的root上,也就是cgroup_setup_root要完成的事:
static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask){css_populate_dir(&root_cgrp->self, NULL);rebind_subsystems(root, ss_mask);}上面的两个函数,在cgroup里是很常用的API,其中,css_populate_dir的声明原型是:
css_populate_dir(struct cgroup_subsys_state *css, struct cgroup *cgrp_override)表示为指定的cgroup创建必要的sysfs子选项回调,比如tasks的read/write回调,以及cpus的read/write回调,
跟这个回调相关的函数集,来源是subsys的回调,对cpuset来说,这些回调来源于上面定义的cpuset_cgrp_subsys
的legacy_cftypes文件。
static int css_populate_dir(struct cgroup_subsys_state *css, struct cgroup *cgrp_override){struct cgroup *cgrp = cgrp_override ?: css->cgroup;if (!css->ss) {if (cgroup_on_dfl(cgrp))cfts = cgroup_dfl_base_files;elsecfts = cgroup_legacy_base_files;return cgroup_addrm_files(&cgrp->self, cgrp, cfts, true);}list_for_each_entry(cfts, &css->ss->cfts, node) {ret = cgroup_addrm_files(css, cgrp, cfts, true);if (ret < 0) {failed_cfts = cfts;goto err;}}}可以看出,逻辑被分成两部分,如果css->ss没有赋值(在cgroup系统刚初始化时,css->ss没有赋值),
那么用的是默认的cgroup_dfl_base_files或者cgroup_legacy_base_files,然后添加到cgroup的对象;
如果css->ss已经赋值了,那么在此基础上还要添加跟子系统相关的css的回调,例如cpuset.开头的成员。
虽然我们整个流程说的是mount流程,为了说明cgroup_setup_root的使用,我们先提一下cgroup初始化函数,
因为这个函数里才会出现css->ss没有赋值的情况:
int __init cgroup_init(void){cgroup_setup_root(&cgrp_dfl_root, 0);for_each_subsys(ss, ssid) {cgroup_init_subsys(ss, false);if (ss->dfl_cftypes == ss->legacy_cftypes) {cgroup_add_cftypes(ss, ss->dfl_cftypes);} else {cgroup_add_dfl_cftypes(ss, ss->dfl_cftypes);cgroup_add_legacy_cftypes(ss, ss->legacy_cftypes);}}}
我们看到cgroup_init一来就调用了cgroup_setup_root,而mask为0,说明只是想
初始化一下cgrp_dfl_root这个cgroup根的回调,并不想跟具体的子系统挂钩。
接下来cgroup_init_subsys(ss, false);会初始化所有的subsys,即为每个subsys分配css,
并将css->ss指回所属的subsys:
static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early){ss->root = &cgrp_dfl_root;css = ss->css_alloc(cgroup_css(&cgrp_dfl_root.cgrp, ss));css->ss = ss;css->cgroup = &cgrp_dfl_root.cgrp;}
接下来是尝试把该subsys对应回调,添加到该subsys包含的css中去,
对cpuset来说,只有一个legacy_ctypes回调,因此将尝试把legacy_cftype添加
到subsys,即添加到ss的cfts链表:
list_add_tail(&cfts->node, &ss->cfts);到这里为止,子系统subsys的cfts文件成员列表就有值了,下一次再执行cgroup_setup_root
的时候,就会把subsys的cfts添加到cgroup的sysfs成员里了。
总结一下,但系统在cgroup初始化时,先调用cgroup_setup_root(&cgrp_dfl_root, 0);,
则生成默认的回调cgroup_dfl_base_files,包含cgroup.controllers,cgroup.events等子条目;
cgroup初始化完毕,用户mount子系统时,例如的cpuset,则子sysfs条目再加上加上cpuset自己的条目,
即cpuset.cpus,cpuset.mems等。
再回到mount流程,还是cgroup_setup_root函数,我们再把这个函数列出来:
static int cgroup_setup_root(struct cgroup_root *root, unsigned long ss_mask){css_populate_dir(&root_cgrp->self, NULL);rebind_subsystems(root, ss_mask);list_add(&root->root_list, &cgroup_roots);}
在mount 流程中,css_populate_dir执行完毕后,cpuset目录已经生成了必要的sysfs条目,接下来,
是redind_subsystems。这个函数的作用是什么?是用新生成的这个cgroup根,接管系统里指定ss_mask
的子系统。注意,在cgroup_init_subsys中,是subsys(ss)和css(cgroup subsys state)关联起来了,但跟新生成的
cgroup还没有联系起来。具体和subsys子系统的绑定是靠rebind_subsystems:
static int rebind_subsystems(struct cgroup_root *dst_root, unsigned long ss_mask){for_each_subsys_which(ss, ssid, &tmp_ss_mask) {struct cgroup *scgrp = &ss->root->cgrp;css_populate_dir(cgroup_css(scgrp, ss), dcgrp);}for_each_subsys_which(ss, ssid, &ss_mask) {struct cgroup_root *src_root = ss->root;struct cgroup *scgrp = &src_root->cgrp;struct cgroup_subsys_state *css = cgroup_css(scgrp, ss);css_clear_dir(css, NULL);RCU_INIT_POINTER(scgrp->subsys[ssid], NULL);rcu_assign_pointer(dcgrp->subsys[ssid], css);ss->root = dst_root;css->cgroup = dcgrp;src_root->subsys_mask &= ~(1 << ssid);dst_root->subsys_mask |= 1 << ssid;if (dst_root == &cgrp_dfl_root) {static_branch_enable(cgroup_subsys_on_dfl_key[ssid]);} else {dcgrp->subtree_control |= 1 << ssid;cgroup_refresh_child_subsys_mask(dcgrp);static_branch_disable(cgroup_subsys_on_dfl_key[ssid]);}}}
首先我们强调了很多次,rebind函数的意思是把submask对应的子系统,全部重新绑定到指定根cgroup上,
假设有两个变量A和B,和一个公用变量C,初始C保存了A的信息,我们要把C的值改成B,并清理A的数据。
于是我们可以分三步,先把A的值拷贝给B,然后可以安全的销毁A,最后把C指向B,这就是rebind函数的思想。
遍历当前系统中的指定待接管subsys,将他们的参数(即各子系统css下面的sysfs回调)赋值给将要替换的cgroup的sysfs,
接着第二次遍历这些子系统,将他们的css下的sysfs条目清空,接着将css应该包含的条目指向新cgroup,最后
将原src_root 即cgroup根的相应子系统掩码去掉,进而由新cgroup根来接管(比如,本来dir A管辖cpuset和memory,然后
dir B要求接管cpuset后,dir A就必须把他子目录里cpuset开头的条目删除,让dir B来生成并管理)这就完成了一次
rebind过程。
还是最早的那个例子,我们曾看到mount的结果:
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,release_agent=/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset,clone_children)其中systemd是没有子系统绑定的,我们到他目录看看:
root@Surface-Pro-3:/sys/fs/cgroup/systemd# lscgroup.clone_children cgroup.procs cgroup.sane_behavior notify_on_release release_agent system.slice tasks user.slic确实都是跟subsys子系统无关的条目,再来看看cpuset的目录:
root@Surface-Pro-3:/sys/fs/cgroup/cpuset# lscgroup.clone_children cpuset.cpus cpuset.mem_hardwall cpuset.memory_spread_page cpuset.sched_relax_domain_level taskscgroup.procs cpuset.effective_cpus cpuset.memory_migrate cpuset.memory_spread_slab notify_on_release user.slicecgroup.sane_behavior cpuset.effective_mems cpuset.memory_pressure cpuset.mems release_agentcpuset.cpu_exclusive cpuset.mem_exclusive cpuset.memory_pressure_enabled cpuset.sched_load_balance system.slice可以看出多了一部分cpuset的条目。
- [知其然不知其所以然-15] cgroup概述
- [知其然不知其所以然-14] cpu hotplug引出的cgroup故障
- Cgroup简介-概述
- LINUX CGROUP 概述
- 知其然不知其所以然!
- Cgroup
- Cgroup
- cgroup
- cgroup
- cgroup--cgroup 测试
- IT--cgroup--cgroup使用
- 知其然不知其所以然之 Hello Android
- [知其然不知其所以然-24] ioremap failure
- [知其然不知其所以然-32] Queued Spinlock
- [知其然不知其所以然-38] task priority
- [知其然不知其所以然-39] Deploy thermald
- Cgroup文件系统
- Cgroup基础知识
- hibernate--关系映射归纳和总结
- 项目进度计划
- ListView 适配器实现getviewtypecount() 数组越界IndexOutOfBoundException
- leetcode笔记:Pow(x, n)
- maven打tar.gz zip包—maven-assembly-plugin
- [知其然不知其所以然-15] cgroup概述
- MyBatis项目-shop购物系统
- Oracle高级查询,GROUP BY
- Xcode7中 http请求报错App Transport Security has blocked a cleartext HTTP
- 虚拟机中使用linux系启用文件共享之后的文件存在的位置
- 2015年年尾总结
- Oracle表分区
- OpenGL 视图变换和投影变换
- xib总结