前置:这里使用的linux版本是4.8,x86体系。

cgroup_init_early();
聊这个函数就需要先了解cgroup。

cgroup概念

这个函数就是初始化cgroup所需要的参数的。cgroup最初是在2006年由google的一名工程师提出的,目的是把一些共同目标的进程放在一个组里面,而这个组里面的进程能共享指定数额的资源。而后就有了cgroup这个概念了。

我们把每种资源叫做子系统,比如CPU子系统,内存子系统。为什么叫做子系统呢,因为它是从整个操作系统的资源衍生出来的。然后我们创建一种虚拟的节点,叫做cgroup,然后这个虚拟节点可以扩展,以树形的结构,有root节点,和子节点。这个父节点和各个子节点就形成了层级(hierarchiy)。每个层级都可以附带继承一个或者多个子系统,就意味着,我们把资源按照分割到多个层级系统中,层级系统中的每个节点对这个资源的占比各有不同。

下面我们想法子把进程分组,进程分组的逻辑叫做css_set。这里的css是cgroup_subsys_state的缩写。所以css_set和进程的关系是一对多的关系。另外,在cgroup眼中,进程请不要叫做进程,叫做task。这个可能是为了和内核中进程的名词区分开吧。

进程分组css_set,不同层级中的节点cgroup也都有了。那么,就要把节点cgroup和层级进行关联,和数据库中关系表一样。这个事一个多对多的关系。为什么呢?首先,一个节点可以隶属于多个css_set,这就代表这这批css_set中的进程都拥有这个cgroup所代表的资源。其次,一个css_set需要多个cgroup。因为一个层级的cgroup只代表一种或者几种资源,而一般进程是需要多种资源的集合体。

美团的这个图片描写的非常清晰,一看就了解了:

task_struct

首先先看进程的结构,里面和cgroup有关的是

#ifdef CONFIG_CGROUPS// 设置这个进程属于哪个css_setstruct css_set __rcu *cgroups;// cg_list是用于将所有同属于一个css_set的task连成一起struct list_head cg_list;
#endif

我们会在代码中经常见到list_head。它其实就是表示,这个在链表中存在。

struct list_head {struct list_head *next, *prev;
};

它的结构很简单,就能把某种相同性质的结构连成一个链表,根据这个链表我能前后找全整个链表或者头部节点等。

css_set

结构体在include/linux/cgroup-defs.h中。

struct css_set {// 引用计数,gc使用,如果子系统有引用到这个css_set,则计数+1atomic_t refcount;// TODO: 列出有相同hash值的cgroup(还不清楚为什么)struct hlist_node hlist;// 将所有的task连起来。mg_tasks代表迁移的任务struct list_head tasks;struct list_head mg_tasks;// 将这个css_set对应的cgroup连起来struct list_head cgrp_links;// 默认连接的cgroupstruct cgroup *dfl_cgrp;// 包含一系列的css(cgroup_subsys_state),css就是子系统,这个就代表了css_set和子系统的多对多的其中一面struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];// 内存迁移的时候产生的系列数据struct list_head mg_preload_node;struct list_head mg_node;struct cgroup *mg_src_cgrp;struct cgroup *mg_dst_cgrp;struct css_set *mg_dst_cset;// 把->subsys[ssid]->cgroup->e_csets[ssid]结构展平放在这里,提高迭代效率struct list_head e_cset_node[CGROUP_SUBSYS_COUNT];// 所有迭代任务的列表,这个补丁参考:https://patchwork.kernel.org/patch/7368941/struct list_head task_iters;// 这个css_set是否已经无效了bool dead;// rcu锁所需要的callback等信息struct rcu_head rcu_head;
};

这里说一下rcu锁,这个锁是linux2.6引入的。它是非常高效的,适合读多写少的情况。全称是(Read-Copy Update)读-拷贝修改。原理就是读操作的时候,不需要任何锁,直接进行读取,写操作的时候,先拷贝一个副本,然后对副本进行修改,最后使用回调(callback)在适当的时候把指向原来数据的指针指向新的被修改的数据。https://www.ibm.com/developerworks/cn/linux/l-rcu/

这里的rcu_head就存储了对这个结构上rcu锁所需要的回调信息。

struct callback_head {struct callback_head *next;void (*func)(struct callback_head *head);
} __attribute__((aligned(sizeof(void *))));
#define rcu_head callback_head

回到css_set,其实最重要的就是cgroup_subsys_state subsys[]数组这个结构。

cgroup_subsys_state 和 cgroup_subsys

这个结构最重要的就是存储的进程与特定子系统相关的信息。通过它,可以将task_struct和cgroup连接起来了:task_struct->css_set->cgroup_subsys_state->cgroup

struct cgroup_subsys_state {// 对应的cgroupstruct cgroup *cgroup;// 子系统struct cgroup_subsys *ss;// 带cpu信息的引用计数(不大理解)struct percpu_ref refcnt;// 父cssstruct cgroup_subsys_state *parent;// 兄弟和孩子链表串struct list_head sibling;struct list_head children;// css的唯一idint id;// 可设置的flag有:CSS_NO_REF/CSS_ONLINE/CSS_RELEASED/CSS_VISIBLEunsigned int flags;// 为了保证遍历的顺序性,设置遍历按照这个字段的升序走u64 serial_nr;// 计数,计算本身css和子css的活跃数,当这个数大于1,说明还有有效子cssatomic_t online_cnt;// TODO: 带cpu信息的引用计数使用的rcu锁(不大理解)struct rcu_head rcu_head;struct work_struct destroy_work;
};

cgroup_subsys结构体在include/linux/cgroup-defs.h里面

struct cgroup_subsys {// 下面的是函数指针,定义了子系统对css_set结构的系列操作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);// 这些函数指针表示了对子系统对进程task的一系列操作int (*can_attach)(struct cgroup_taskset *tset);void (*cancel_attach)(struct cgroup_taskset *tset);void (*attach)(struct cgroup_taskset *tset);void (*post_attach)(void);int (*can_fork)(struct task_struct *task);void (*cancel_fork)(struct task_struct *task);void (*fork)(struct task_struct *task);void (*exit)(struct task_struct *task);void (*free)(struct task_struct *task);void (*bind)(struct cgroup_subsys_state *root_css);// 是否在前期初始化了bool early_init:1;// 如果设置了true,那么在cgroup.controllers和cgroup.subtree_control就不会显示, TODO:bool implicit_on_dfl:1;// 如果设置为false,则子cgroup会继承父cgroup的子系统资源,否则不继承或者只继承一半// 但是现在,我们规定,不允许一个cgroup有不可继承子系统仍然可以衍生出cgroup。如果做类似操作,我们会根据// warned_broken_hierarch出现错误提示。bool broken_hierarchy:1;bool warned_broken_hierarchy:1;int id;const char *name;// 如果子cgroup的结构继承子系统的时候没有设置name,就会沿用父系统的子系统名字,所以这里存的就是父cgroup的子系统名字const char *legacy_name;struct cgroup_root *root;  // 这个就是子系统指向的层级中的root的cgroupstruct idr css_idr; // 对应的css的idr// 对应的文件系统相关信息struct list_head cfts;struct cftype *dfl_cftypes;    /* 默认的文件系统 */struct cftype *legacy_cftypes;    /* 继承的文件系统 */// 有的子系统是依赖其他子系统的,这里是一个掩码来表示这个子系统依赖哪些子系统unsigned int depends_on;
};

这里特别说一下cftype。它是cgroup_filesystem_type的缩写。这个要从我们的linux虚拟文件系统说起(VFS)。VFS封装了标准文件的所有系统调用。那么我们使用cgroup,也抽象出了一个文件系统,自然也需要实现这个VFS。实现这个VFS就是使用这个cftype结构。

这里说一下idr。这个是linux的整数id管理机制。你可以把它看成一个map,这个map是把id和制定指针关联在一起的机制。它的原理是使用基数树。一个结构存储了一个idr,就能很方便根据id找出这个id对应的结构的地址了。http://blog.csdn.net/dlutbrucezhang/article/details/10103371

cgroup

cgroup结构也在相同文件,但是cgroup_root和子节点cgroup是使用两个不同结构表示的。

struct cgroup {// cgroup所在cssstruct cgroup_subsys_state self;unsigned long flags;int id;// 这个cgroup所在层级中,当前cgroup的深度int level;// 每当有个非空的css_set和这个cgroup关联的时候,就增加计数1int populated_cnt;struct kernfs_node *kn;        /* cgroup kernfs entry */struct cgroup_file procs_file;    /* handle for "cgroup.procs" */struct cgroup_file events_file;    /* handle for "cgroup.events" */// TODO: 不理解u16 subtree_control;u16 subtree_ss_mask;u16 old_subtree_control;u16 old_subtree_ss_mask;// 一个cgroup属于多个css,这里就是保存了cgroup和css直接多对多关系的另一半struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT];// 根cgroupstruct cgroup_root *root;// 相同css_set的cgroup链表struct list_head cset_links;// 这个cgroup使用的所有子系统的每个链表struct list_head e_csets[CGROUP_SUBSYS_COUNT];// TODO: 不理解struct list_head pidlists;struct mutex pidlist_mutex;// 用来保存下线taskwait_queue_head_t offline_waitq;// TODO: 用来保存释放任务?(不理解)struct work_struct release_agent_work;// 保存每个level的祖先int ancestor_ids[];
};

这里看到一个新的结构,wait_queue_head_t,这个结构是用来将一个资源挂在等待队列中,具体参考:http://www.cnblogs.com/lubiao/p/4858086.html

还有一个结构是cgroup_root

struct cgroup_root {// TODO: 不清楚struct kernfs_root *kf_root;// 子系统掩码unsigned int subsys_mask;// 层级的idint hierarchy_id;// 根部的cgroup,这里面就有下级cgroupstruct cgroup cgrp;// 相等于cgrp->ancester_ids[0]int cgrp_ancestor_id_storage;// 这个root层级下的cgroup数,初始化的时候为1atomic_t nr_cgrps;// 串起所有的cgroup_rootstruct list_head root_list;unsigned int flags;// TODO: 不清楚struct idr cgroup_idr;// TODO: 不清楚char release_agent_path[PATH_MAX];// 这个层级的名称,有可能为空char name[MAX_CGROUP_ROOT_NAMELEN];
};

cgroup_init_early

回到这个函数

int __init cgroup_init_early(void)
{// 初始化cgroup_root,就是一个cgroup_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) {WARN(!ss->css_alloc || !ss->css_free || ss->name || ss->id,"invalid cgroup_subsys %d:%s css_alloc=%p css_free=%p id:name=%d:%s\n",i, cgroup_subsys_name[i], ss->css_alloc, ss->css_free,ss->id, ss->name);WARN(strlen(cgroup_subsys_name[i]) > MAX_CGROUP_TYPE_NAMELEN,"cgroup_subsys_name %s too long\n", cgroup_subsys_name[i]);ss->id = i;ss->name = cgroup_subsys_name[i];if (!ss->legacy_name)ss->legacy_name = cgroup_subsys_name[i];if (ss->early_init)cgroup_init_subsys(ss, true);}return 0;
}

这个函数初始化的cgroup_root是一个全局的变量。定义在kernel/cgroup.c中。

struct cgroup_root cgrp_dfl_root;
EXPORT_SYMBOL_GPL(cgrp_dfl_root);

理解了cgroup结构,里面的设置就可以基本看懂了。

参考

http://files.cnblogs.com/files/lisperl/cgroups%E4%BB%8B%E7%BB%8D.pdf
http://tech.meituan.com/cgroups.html
http://coolshell.cn/articles/17049.html

转载于:https://www.cnblogs.com/muahao/p/10280998.html

cgroup代码浅析(1)相关推荐

  1. cgroup代码浅析(2)

    info include/linux/memcontrol.h memcg相关的函数 数据结构 mem_cgroup在每个node下,都有一个lruvec, 这个lruvec保存在mem_cgroup ...

  2. 来了!PostgreSQL 同步流复制原理和代码浅析,请签收

    摘要: 数据库 ACID 中的持久化如何实现 数据库 ACID 里面的 D,持久化. 指的是对于用户来说提交的事务,数据是可靠的,即使数据库 crash了,在硬件完好的情况下,也能恢复回来.Postg ...

  3. 美狐美颜SDK动态贴纸代码浅析

    如今,在美颜的刚需下,各大平台开始接入美颜SDK,接入美颜SDK是因为该平台希望能在竞争中脱颖而出并吸引更多用户,而现如今基本的美容和美容功能已无法满足用户的需求,还需要各种趣味功能,例如动态贴纸的帮 ...

  4. 巧用InTouch 实现多级菜单管理及其核心代码浅析

    写在面前 原文内容:巧用InTouch 实现多级菜单管理及其核心代码浅析 前面我们分享了西家,罗家,和施家组态软件与SQL SERVER数据库的通讯: 罗克韦尔FactoryTalk View Stu ...

  5. spring初始化源码浅析之代码浅析

    目录 1.refresh()简介 2.关键代码跟踪 ​2.1.obtainFreshBeanFactory()代码分析 2.2.invokeBeanFactoryPostProcessors(bean ...

  6. BERT融合知识图谱之模型及代码浅析

    出品:贪心科技AI 作者:高阶NLP6期学员,吕工匠 最近小编在做一个文本分类的项目,相比一般的文本分类任务,其挑战之处在于: 1)特征很少:训练数据的文本很短,3~5个字符 2)类别很多:>3 ...

  7. php文件下载到服务器代码,PHP文件下载实例代码浅析

    文件下载的功能对一个网站而言基本上是必备的了,今天就来看看php是如何实现文件下载的吧. 无控制类型 这里说的无控制类型是指 没有添加php代码控制的资源可以被直接下载的那些类型.一般而言,压缩文件. ...

  8. 电脑蓝屏终止代码irql_电脑蓝屏代码浅析

    你不一定要点蓝字关注我的 01 什么是蓝屏 蓝屏 BSOD 蓝屏,英文名BSOD,Blue Screen of Death,即蓝屏死机,微软操作系统的经典死机提示屏幕. 蓝屏其实是一种保护措施和一种调 ...

  9. python乘法口诀代码-浅析一句python代码成生九九乘法表

    print(' '.join([' '.join('%sx%s=%-2s'%(x,y,x*y) for x in range(1,y+1)) for y in range(1,10)])) 最近在os ...

  10. zookeeper代码浅析

    ZooKeeper是近期比较热门的一个类Paxos实现.也是一个逐渐得到广泛应用的开源的分布式锁服务实现.被认为是Chubby的开源版,虽然具体实现有很多差异.ZooKeeper概要的介绍可以看官方文 ...

最新文章

  1. Redis学习笔记 - 数据类型与API(1)Key
  2. python类、对象、方法、属性之类与对象笔记
  3. Vue轻松入门,一起学起来!
  4. HTML5 audio 标签-在html中定义声音的标签
  5. 【计网】计算机网络期末总复习-谢希仁(个人总结)理论概念
  6. 我的MIDAS中间层服务器运行效果图
  7. 大型WEB架构概况(笔记整理)一
  8. QT中事件发送函数sendEvent()、postEvent()详解
  9. 美国美国,USA USA
  10. mssql 跨库获取mysql
  11. 如何只保留1000条数据mysql_php实现只保留mysql中最新1000条记录
  12. 菜鸟的学习之路(8) — 数组(Array)
  13. 应用HTML5 a标签下载文件
  14. 联通4g满格但是网速慢_4G信号明明满格,但网速却慢过蜗牛,是什么原因?
  15. 软件测试工程师的职业技能分析
  16. 一封华为离职IT总监的告别信
  17. 工业平板手持PDA的功能有哪些?
  18. IOS 图片拉伸不变形
  19. 【react native】Flatlist实现上拉滚动加载
  20. C语言基础——运算符(定义变量、转义字符、输入输出语句、运算符、32个关键字)

热门文章

  1. RabbitMQ学习——基本概念
  2. 豆瓣评分9.3的经典Kubernetes图书推出中文版,学习K8S更方便了
  3. jBPM4工作流应用开发指南
  4. 中文分词:正向最大匹配与逆向最大匹配
  5. python命令行参数解析模块argparse和docopt
  6. 千锋培训php怎么样,零基础学员真实感受 选择千锋PHP培训完成人生蜕变
  7. Ubuntu chmod 命令
  8. 创建虚拟文件系统设备文件节点_使用proc_create实例分析
  9. python多版本共存失败记录
  10. sssp-springmvc+spring+spring-data-jpa问题总结