cgroups的实现

1. cgroups体系结构

每个内核的子系统如果想要挂载到cgroup系统中,必须要先拥有一个cgroup_subsys对象,通过这个对象将对子系统资源的操作函数以接口的形式进行约定,各个子系统再根据自己的需求去实现这些接口,除了这些函数指针,cgroup_subsys结构体还包括id, name, early_init等属性,分别表示该子系统在cgroup->subsys[]数组中的索引,子系统名称,在系统启动时是否需要尽早初始化等。

struct cgroup_subsys{struct cgroup_subsys_state* (*css_alloc)(struct cgroup_subsys_state *parent_css);void (*attach)(struct cgroup_taskset *tset);...bool early_init: 1;ind id;const char *name;struct cgroup_root *root;
}

除了这些,cgroup_subsys还有一个指向cgroup_root类型的root指针,cgroup_root表示一个cgroup hierarchy的根,注意与根结点相区别,所以说一个子系统只能通过cgroup_root结构与一个cgroup hierarchy绑定(但是反过来一个cgroup hierarchy可以绑定多个子系统 )。关于cgroup_root下面具体讨论。

再看第一个函数指针css_alloc,其返回了一个cgroup_subsys_state结构体,每一个注册到系统中的cgroup子系统,除了代表它的cgroup_subsys,还有与之相关联的cgroup_subsys_state,简称css,用来表示某个子系统与某个cgroup相关联的状态,将一个子系统与一个cgroup相连接。每个subsys会对调用css_alloc的cgroup分配一个自己的cgroup_subsys_state结构。

struct cgroup_subsys_state {/* PI: the cgroup that this css is attached to */struct cgroup *cgroup;/* PI: the cgroup subsystem that this css is attached to */struct cgroup_subsys *ss;/* reference count - access via css_[try]get() and css_put() */struct percpu_ref refcnt;/* siblings list anchored at the parent's ->children */struct list_head sibling;struct list_head children;/** PI: Subsys-unique ID.  0 is unused and root is always 1.  The* matching css can be looked up using css_from_id().*/int id;unsigned int flags;...struct cgroup_subsys_state *parent;
};
  • cgroup指向此css所关联的cgroup
  • ss指向此css所关联的cgroup_subsys
  • sibling是一个双向链表,保存所关联的cgroup拥有的其他css
  • parent指向父css的指针

提到css,那就不得不提css_set,css_set是保存指向cgroup_subsys_state对象的一组引用计数指针的集合,用来保存与task相关的cgroup信息,同时也代表了一组子系统资源的组合,css_set的使用节省了cgroups在task_struct结构体中所占用的空间,而且加快了task fork()/exit()的速度。

struct css_set {/** Set of subsystem states, one for each subsystem. This array is* immutable after creation apart from the init_css_set during* subsystem registration (at boot time).*/struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT];/* reference count */refcount_t refcount;.../** Lists running through all tasks using this cgroup group.*/struct list_head tasks;/** List running through all cgroup groups in the same hash* slot. Protected by css_set_lock*/struct hlist_node hlist;/** List of cgrp_cset_links pointing at cgroups referenced from this* css_set.  Protected by css_set_lock.*/struct list_head cgrp_links;/* dead and being drained, ignore for migration */bool dead;/* For RCU-protected deletion */struct rcu_head rcu_head;
};
  • subsys保存子系统状态的集合,初始化后不可修改
  • hlist用来链接同一个hashtable slots中的所有css_set
  • refcount用来引用计数
  • task用来链接所有使用此css_settask_struct集合

从task到cgroup的关联

从task到其所属的cgroup之间是没有直接指针相连接的,但是task可以通过一个媒介来获取其所属的cgroup,这个媒介就是css_setcgroup_subsys_state。通过task_struct -> cgroups -> subsys[ssid] ->cgroup即可访问到管理对应子系统的cgroup。之所以这么设计时因为获取子系统状态的操作预计会频繁发生,而且是在性能关键代码中,然而需要一个task实际的cgroup来执行的操作(尤其是task在cgroups之间迁移的操作)则并没有那么常见。task_struct中的cg_list则是用来连接使用同一个css_set的task的链表,css_set通过tasks来遍历访问此链表。

css_set与cgroup之间的关联

一个进程属于一个css_set, 一个css_set就存储了一组进程跟各个子系统相关的信息,但是这些信息有可能不是从一个cgroup那里获得的,因为一个进程可以同时属于几个cgroup,只要这些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

因此它们之间的关系是 M ∗ N M*N M∗N , 这种关系的表达则是通过cgrp_cset_link来实现

struct cgrp_cset_link {/* the cgroup and css_set this link associates */struct cgroup        *cgrp;struct css_set        *cset;/* list of cgrp_cset_links anchored at cgrp->cset_links */struct list_head cset_link;/* list of cgrp_cset_links anchored at css_set->cgrp_links */struct list_head  cgrp_link;
};

在回到我们一开始所讲的cgroup_subsys结构,其拥有的一个属性名为root,指向一个cgroup_root结构体

struct cgroup_root {struct kernfs_root *kf_root;/* The bitmask of subsystems attached to this hierarchy */unsigned int subsys_mask;/* Unique id for this hierarchy. */int hierarchy_id;/* The root cgroup. Root is destroyed on its release. */struct cgroup cgrp;/* for cgrp->ancestor_ids[0] */u64 cgrp_ancestor_id_storage;/* Number of cgroups in the hierarchy, used only for /proc/cgroups */atomic_t nr_cgrps;/* A list running through the active hierarchies */struct list_head root_list;/* Hierarchy-specific flags */unsigned int flags;/* The path to use for release notifications. */char release_agent_path[PATH_MAX];/* The name for this hierarchy - may be empty */char name[MAX_CGROUP_ROOT_NAMELEN];
};

一个cgroup_root是一个层级的根,是cgroup的核心,且不由controller直接操作。

  • kf_root:
  • subsys_mask:与该层级相关联的子系统的点位图
  • hierarchy_id:层级的ID
  • cgrp:层级的根节点
  • root_list:包含所有层级的链表

接下来引入主角cgroup

struct cgroup {/* self css with NULL ->ss, points back to this cgroup */struct cgroup_subsys_state self;unsigned long flags;      /* "unsigned long" so bitops work *//** The depth this cgroup is at.  The root is at depth zero and each* step down the hierarchy increments the level.  This along with* ancestor_ids[] can determine whether a given cgroup is a* descendant of another without traversing the hierarchy.*/int level;/* Maximum allowed descent tree depth */int max_depth;...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" *//* Private pointers for each registered subsystem */struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT];struct cgroup_root *root;/** List of cgrp_cset_links pointing at css_sets with tasks in this* cgroup.  Protected by css_set_lock.*/struct list_head cset_links;/* per-cpu recursive resource statistics */struct cgroup_rstat_cpu __percpu *rstat_cpu;struct list_head rstat_css_list;/* cgroup basic resource statistics */struct cgroup_base_stat last_bstat;struct cgroup_base_stat bstat;struct prev_cputime prev_cputime;  /* for printing out cputime *//** list of pidlists, up to two for each namespace (one for procs, one* for tasks); created on demand.*/struct list_head pidlists;struct mutex pidlist_mutex;/* used to wait for offlining of csses */wait_queue_head_t offline_waitq;/* used to schedule release agent */struct work_struct release_agent_work;/* If there is block congestion on this cgroup. */atomic_t congestion_count;/* Used to store internal freezer state */struct cgroup_freezer_state freezer;/* ids of the ancestors at each level including self */u64 ancestor_ids[];
};
  • sibling, children, parent三个list_head负责将同一层级的cgroup连接为一颗cgroup树
  • subsys存储一组指向cgroup_subsys_state的指针
  • root指向层级所对应的cgroup_root结构体
  • cset_links指向cgrp_cset_link连成的链表,负责与css_set相连接

2. cgroup文件系统

cgroup v1,v2的文件系统类型的数据结构

struct file_system_type cgroup_fs_type = {.name = "cgroup",.mount = cgroup_mount,.kill_sb = cgroup_kill_sb,.fs_flags = FS_USERNS_MOUNT,
};static struct file_system_type cgroup2_fs_type = {.name = "cgroup2",.mount = cgroup_mount,.kill_sb = cgroup_kill_sb,.fs_flags = FS_USERNS_MOUNT,
};

cgroup文件系统其实是通过kernfs来实现的,通过文件接口的形式将内核中cgroup信息以及子系统信息传递给用户态,用户照样通过调用vfs接口来进行一般的读写操作。

以cgroup创建为例,内核创建一个cgroup首先调用cgroup_mkdir,cgroup_mkdir调用cgroup_create和css_populate_dir,cgroup_create调用kernfs_create_dir在cgroup文件系统中创建cgroup对应的目录,css_populate_dir又调用cgroup_addrm_dir在对应cgroup目录中填充对应子系统控制文件。

那么是如果通过向控制文件中写入或删除字符(串)来实现资源控制呢?答案就在struct cftype中,cftype在源码中被定义为handler for definitions for cgroup control files。每个子系统通过实现自己的cftype数组(每个控制文件都对应一个cftype结构体,所以每个cgroup_subsys中都保存了一个cftype数组)来实现各自的资源分配功能。

其具体定义如下:

struct cftype {/** By convention, the name should begin with the name of the* subsystem, followed by a period.  Zero length string indicates* end of cftype array.*/char name[MAX_CFTYPE_NAME];unsigned long private;/** The maximum length of string, excluding trailing nul, that can* be passed to write.  If < PAGE_SIZE-1, PAGE_SIZE-1 is assumed.*/size_t max_write_len;/* CFTYPE_* flags */unsigned int flags;/** If non-zero, should contain the offset from the start of css to* a struct cgroup_file field.  cgroup will record the handle of* the created file into it.  The recorded handle can be used as* long as the containing css remains accessible.*/unsigned int file_offset;/** Fields used for internal bookkeeping.  Initialized automatically* during registration.*/struct cgroup_subsys *ss;    /* NULL for cgroup core files */struct list_head node;      /* anchored at ss->cfts */struct kernfs_ops *kf_ops;int (*open)(struct kernfs_open_file *of);void (*release)(struct kernfs_open_file *of);/** read_u64() is a shortcut for the common case of returning a* single integer. Use it in place of read()*/u64 (*read_u64)(struct cgroup_subsys_state *css, struct cftype *cft);/** read_s64() is a signed version of read_u64()*/s64 (*read_s64)(struct cgroup_subsys_state *css, struct cftype *cft);/* generic seq_file read interface */int (*seq_show)(struct seq_file *sf, void *v);/* optional ops, implement all or none */void *(*seq_start)(struct seq_file *sf, loff_t *ppos);void *(*seq_next)(struct seq_file *sf, void *v, loff_t *ppos);void (*seq_stop)(struct seq_file *sf, void *v);/** write_u64() is a shortcut for the common case of accepting* a single integer (as parsed by simple_strtoull) from* userspace. Use in place of write(); return 0 or error.*/int (*write_u64)(struct cgroup_subsys_state *css, struct cftype *cft,u64 val);/** write_s64() is a signed version of write_u64()*/int (*write_s64)(struct cgroup_subsys_state *css, struct cftype *cft,s64 val);/** write() is the generic write callback which maps directly to* kernfs write operation and overrides all other operations.* Maximum write size is determined by ->max_write_len.  Use* of_css/cft() to access the associated css and cft.*/ssize_t (*write)(struct kernfs_open_file *of,char *buf, size_t nbytes, loff_t off);__poll_t (*poll)(struct kernfs_open_file *of,struct poll_table_struct *pt);#ifdef CONFIG_DEBUG_LOCK_ALLOCstruct lock_class_key lockdep_key;
#endif
};

可以看到cfytpe定义了很多文件的函数指针,目的就是将用户对控制文件输入的字符(串)转换为对进程的资源管控,每一个cgroup 目录下的控制文件对应的cftype都包含cgroup_base_files + css -> ss -> cfts,分别对应来自cgroup和来自子系统的控制文件,cgroup的控制文件一般以cgroup.开头,来自子系统的控制文件一般以子系统的名称开头。

cgroup通过调用cgroup_addrm_file来添加删除控制文件,并将cftype保存到对应kernfs文件

cftype flags的类型

/* cftype->flags */
enum {CFTYPE_ONLY_ON_ROOT   = (1 << 0),  /* only create on root cgrp */CFTYPE_NOT_ON_ROOT    = (1 << 1),  /* don't create on root cgrp */CFTYPE_NS_DELEGATABLE   = (1 << 2),  /* writeable beyond delegation boundaries */CFTYPE_NO_PREFIX    = (1 << 3),  /* (DON'T USE FOR NEW FILES) no subsys prefix */CFTYPE_WORLD_WRITABLE  = (1 << 4),  /* (DON'T USE FOR NEW FILES) S_IWUGO */CFTYPE_DEBUG        = (1 << 5),  /* create when cgroup_debug *//* internal flags, do not use outside cgroup core proper */__CFTYPE_ONLY_ON_DFL   = (1 << 16), /* only on default hierarchy */__CFTYPE_NOT_ON_DFL  = (1 << 17), /* not on default hierarchy */
};

cgroups的实现相关推荐

  1. 5分钟了解Docker原理(2),最简单的cgroups介绍!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 很多接触Docker的同学,都接触过cgroup这个名词. ...

  2. cgroups是什么

    cgroups 实现了对资源的配额和度量. cgroups 的使用非常简单,提供类似文件的接口,在 /cgroup目录下新建一个文件夹即可新建一个group,在此文件夹中新建task文件,并将pid写 ...

  3. Kubernetes学习笔记二:Namespace,Cgroups 的隔离与应用

    Kubernetes学习系列文章:Kubernetes-博客专栏 今天在学习极客时间专栏:<深入剖析Kubernetes> 第五讲05 | 白话容器基础(一):从进程说开去和第六讲06 | ...

  4. Docker安全性支持(使用Cgroups机制实现容器资源控制)

    前言 Docker作为最重视安全的容器技术之一,在很多方面都提供了强安全性的默认配置,其中包括:容器root用户的Capability能力限制.Seccomp系统调用过滤.Apparmor的 MAC ...

  5. Linux 操作系统原理 — cgroups 进程资源配额与管理

    目录 文章目录 目录 cgroups cgroups 的子系统 cgroups 的层级结构 cgroups 与进程 cgroups 与文件系统 cgroups 的使用 挂载 cgroups File ...

  6. LXC linux容器简介——在操作系统层次上为进程提供的虚拟的执行环境,限制其使用的CPU和mem等资源,底层是linux内核资源管理的cgroups子系统...

    1.LXC是什么? LXC是Linux containers的简称,是一种基于容器的操作系统层级的虚拟化技术. 2.LXC可以做什么? LXC可以在操作系统层次上为进程提供的虚拟的执行环境,一个虚拟的 ...

  7. Docker cgroups作用(十)

    实现 cgroups的主要目的是为不同用户层面的资源管理,提供一个统一化的接口.从单个任务的资源控制到操作系统层面的虚拟化, groups提供了以下四大功能. 资源限制:groups可以对任务使用的资 ...

  8. Docker Cgroups

    Docker中利用namespace来做权限控制,用cgroups来做资源控制,这两个都是非常重要的,下面是整理了一些关于cgroups相关的. cgroups全称control groups ,是L ...

  9. Docker核心原理之cgroups

    cgroups资源限制 上一篇文章中,我们了解了Docker的资源隔离技术namespace,通过系统调用构建了一个相对隔离的shell环境.也可以称之为一个简单的容器.接下来将讲解另一个强大的内核工 ...

  10. Linux 交换内存空间原理(swap)(Linux内存管理)(cgroups)

    文章目录 什么是swap? 为什么需要swap? swap的缺点? 到底要不要swap? 内存不够用 内存勉强够用 内存充裕 桌面环境 服务器环境 swap大小配置多少比较合适? 怎么配置swap? ...

最新文章

  1. python 排序算法 简书_Python---简析八大排序算法
  2. 【专场报名】七牛云音画质量优化专场
  3. C++类的内联成员函数应放在哪
  4. ELV局部视图与差分隐私【敏感度到底怎么理解】【下】
  5. 检测Web浏览器上的内存泄漏
  6. ROS : RoboWare Studio远程部署
  7. Java并发编程:线程的基本状态
  8. pagesize==0异常问题
  9. EasyExcel自动设置列宽
  10. java摄影网站源码_基于jsp的摄影网站-JavaEE实现摄影网站 - java项目源码
  11. 如何在Windows 10上修复缩略图问题
  12. 手风琴 html 左边,Layui手风琴左边的符号如何修改
  13. R与指数分布(1) 概率密度函数
  14. 软件项目报价术语总结(功能点计数元素ILF、EIF、IE、EO、EQ)
  15. OpenOCD-JTAG调试
  16. 51单片机——读写AT24c64
  17. 教你如何用手机下载视频号[微信小程序]中的视频
  18. python调用jsonrpc接口_微信小程序通过jsonrpc调用python服务端接口
  19. 磷脂PEG化靶向蛋白肽系列 DSPE-PEG- RGR(CRGRRST)/ TH/ R8/ NGR 为华生物提供
  20. 除,除以和被除的区别

热门文章

  1. 计算机hppusg.exe应用程序错误,spoolsv.exe-应用程序错误
  2. 读杨绛先生的《我们仨》部分片段
  3. 如何在Release状态下进行调试
  4. Linux-Shell(六)
  5. 2008服务器系统ie,windows server 2008 IE增强的安全配置关闭方法
  6. JS通过List列表生成树结构
  7. windows应用程序与控制台应用程序的区别
  8. [工具类] 系列二 Lettuce 访问Redis 工具类 RedisUtil
  9. 教你一招:Win10切换输入法与Win7一样(Ctrl + 空格)
  10. Linux与TPM2芯片