Cgroup学习笔记3—代码实现—相关结构和全局变量
基于 LInux-5.10
一、相关结构
1. 通过多次的 #define 和 #undef SUBSYS 宏来展开 cgroup_subsys.h 中通过 deconfig 使能的 cgroup 子系统来填充相关结构体。 在 include/linux/cgroup-defs.h 中以 SUBSYS(_x) 定义的宏,代入 cpu cgroup 展开为:
enum cgroup_subsys_id { ... cpu_cgrp_id, ... CGROUP_SUBSYS_COUNT, };
在 include/linux/cgroup.h 中以 SUBSYS(_x) 定义的宏,代入 cpu 这个 cgroup 子系统,展开为:
extern struct cgroup_subsys cpu_cgrp_subsys; //定义在kernel/sched/core.c extern struct static_key_true cpu_cgrp_subsys_enabled_key; extern struct static_key_true cpu_cgrp_subsys_on_dfl_key;
kernel/cgroup/cgroup.c 中以 SUBSYS(_x) 定义的宏,代入 cpu 这个 cgroup 子系统,展开为:
struct cgroup_subsys *cgroup_subsys[] = { [cpu_cgrp_id] = &cpu_cgrp_subsys, .... }; static const char *cgroup_subsys_name[] = { [cpu_cgrp_id] = "cpu", }; /* 就是一个static key,只不过需要用 static_key_true()判断key是真还是假 */ DEFINE_STATIC_KEY_TRUE(cpu_cgrp_subsys_enabled_key); DEFINE_STATIC_KEY_TRUE(cpu_cgrp_subsys_on_dfl_key); EXPORT_SYMBOL_GPL(cpu_cgrp_subsys_enabled_key); EXPORT_SYMBOL_GPL(cpu_cgrp_subsys_on_dfl_key); static struct static_key_true *cgroup_subsys_enabled_key[] = { { [cpu_cgrp_id] = &cpu_cgrp_subsys_enabled_key, ... } static struct static_key_true *cgroup_subsys_on_dfl_key[] = { [cpu_cgrp_id] = &cpu_cgrp_subsys_on_dfl_key, ... };
从这里可以看到每个 cgroup 子系统的 xx_cgrp_id 值和其在 cgroup_subsys.h 中在使能的部分中的位置有关。名字字符串也是直接来自 cgroup_subsys.h 中。cgroup core 通过 cgroup_subsys 数组对各子系统进行引用。#########
2. struct cgroup_file
不要从 cgroup 核心外部访问任何字段
/* * cgroup_file 是在 cgroup 中创建的文件实例的句柄,例如,用于生成文件更改通知。 * 这可以通过设置 cftype->file_offset 来获得。 */ struct cgroup_file { //cgroup-defs.h struct kernfs_node *kn; unsigned long notified_at; struct timer_list notify_timer; };
3. struct cgroup_subsys_state
struct cgroup_subsys_state { struct cgroup *cgroup; //PI struct cgroup_subsys *ss; //PI struct percpu_ref refcnt; struct list_head sibling; struct list_head children; struct list_head rstat_css_node; int id; //PI unsigned int flags; u64 serial_nr; atomic_t online_cnt; struct work_struct destroy_work; struct rcu_work destroy_rwork; struct cgroup_subsys_state *parent; };
系统维护的 per-subsystem/per-cgroup 状态。这是控制器处理的基本结构构建块。标有“PI:”的成员是公开的和不可变的,可能不带同步的直接访问。
cgroup: PI:这个css附加到的cgroup
ss: PI:这个css附加到的cgroup子系统
refcnt: 引用计数 - 通过 css_[try]get() 和 css_put() 访问
sibling: 锚定在 parent->children 的兄弟列表
rstat_css_node: 刷新锚定在 cgrp->rstat_css_list 上的目标列表
id: PI:子系统唯一 ID。 0 未使用,root 始终为 1。可以使用 css_from_id() 查找匹配的 css。cgroup_init_subsys()中标记需要early初始化的为1,否则是idr分配的一个,但随后early init的又在 cgroup_init()中赋值为idr分配的了。
flags: 标志,如 CSS_NO_REF
serial_nr: 单调递增的唯一序列号,它定义了所有 css 之间的统一顺序。保证所有 ->children 列表都按 ->serial_nr 的升序排列,并用于允许打断和resume遍历。init_and_link_css()中初始化为 css_serial_nr_next++
online_cnt: 由online的自己和孩子增加。用于保证parent不在孩子之前offline。
destroy_work: percpu_ref killing 和 RCU 释放。
parent: 父CSS。放在这里是为了缓存接近包含结构的以下字段。
4. struct css_set
struct css_set { struct cgroup_subsys_state *subsys[CGROUP_SUBSYS_COUNT]; refcount_t refcount; struct css_set *dom_cset; struct cgroup *dfl_cgrp; int nr_tasks; struct list_head tasks; struct list_head mg_tasks; struct list_head dying_tasks; struct list_head task_iters; struct list_head e_cset_node[CGROUP_SUBSYS_COUNT]; struct list_head threaded_csets; struct list_head threaded_csets_node; struct hlist_node hlist; struct list_head cgrp_links; 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; bool dead; struct rcu_head rcu_head; };
css_set 是一个包含指向一组 cgroup_subsys_state 对象的指针的结构。这节省了 task_struct 中的空间并加快了 fork()/exit(),因为单个 inc/dec 和 list_add()/del() 可以增加任务的整个 cgroup set 的引用计数。
subsys[]: 一组子系统的状态,每个子系统一个。 除了子系统注册期间(启动时)的 init_css_set 之外,该数组在创建后是不可变的。cgroup_init_subsys()中: init_css_set.subsys[ss->id] = css
dom_cset: 对于 domain cgroup,以下指向自己. 如果线程化,则到最近域祖先的匹配cset。dom_cset 提供对域 cgroup 及其 csses 的访问,域级资源消耗应计入其中。
dfl_cgrp: 与此 css_set 关联的默认 cgroup。
nr_tasks: 内部任务计数,受 css_set_lock 保护
tasks: 列出使用此 cgroup 组运行的所有任务。
mg_tasks: 列出了属于此 cset 但正在迁移出或迁移入的任务。被 css_set_rwsem 保护,但是,在迁移过程中,一旦将任务移动到 mg_tasks,就可以在持有 cgroup_mutex 的同时安全地读取它。
task_iters: 当前正在执行此 cset 的所有 css_task_iters
e_cset_node[]: 在默认层次结构中,->subsys[ssid] 可能指向附加到祖先的 css,而不是与 css_set 关联的 cgroup。 以下节点锚定在 ->subsys[ssid]->cgroup->e_csets[ssid] 并提供了一种方法来遍历所有附加到给定 cgroup 的 css。
threaded_csets: ->dom_cset 指向此 cset 的所有线程 cset
hlist: 列出在同一哈希slot中运行的所有 cgroup 组。 受 css_set_lock 保护
cgrp_links: 指向从此 css_set 引用的 cgroups 的 cgrp_cset_links 列表。受 css_set_lock 保护。
mg_preload_node: 列出作为源或目标,参与正在进行的迁移的 cset 列表。受 cgroup_mutex 保护
mg_src_cgrp/mg_dst_cgrp/mg_dst_cset: 如果此 cset 充当迁移的源,则设置以下两个字段。mg_src_cgrp 和 mg_dst_cgrp 分别是正在进行的迁移的源 cgroup 和目标 cgroup。mg_dst_cset 是此 cset 上的目标任务应迁移到的目标 cset。受 cgroup_mutex 保护。
dead: 死亡和被清理,迁移时忽略它。
rcu_head: 用于RCU保护下的删除。
5. struct cgroup
struct cgroup { struct cgroup_subsys_state self; unsigned long flags; int level; int max_depth; int nr_descendants; int nr_dying_descendants; int max_descendants; int nr_populated_csets; int nr_populated_domain_children; int nr_populated_threaded_children; int nr_threaded_children; 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" */ u16 subtree_control; u16 subtree_ss_mask; u16 old_subtree_control; u16 old_subtree_ss_mask; struct cgroup_subsys_state __rcu *subsys[CGROUP_SUBSYS_COUNT]; struct cgroup_root *root; struct list_head cset_links; struct list_head e_csets[CGROUP_SUBSYS_COUNT]; struct cgroup *dom_cgrp; struct cgroup *old_dom_cgrp; /* used while enabling threaded */ struct cgroup_rstat_cpu __percpu *rstat_cpu; struct list_head rstat_css_list; struct cgroup_base_stat last_bstat; struct cgroup_base_stat bstat; struct prev_cputime prev_cputime; /* for printing out cputime */ struct list_head pidlists; struct mutex pidlist_mutex; wait_queue_head_t offline_waitq; struct work_struct release_agent_work; struct psi_group psi; struct cgroup_bpf bpf; atomic_t congestion_count; struct cgroup_freezer_state freezer; u64 ancestor_ids[]; };
self: 带有 NULL ->ss 的 self css,指向这个 cgroup
flags: 位掩码,init_cgroup_root()中若 ctx->cpuset_clone_children 为真就设置上 CGRP_CPUSET_CLONE_CHILDREN
level: 此 cgroup 所在的深度。根的深度为零,层次结构的每一层都会增加深度。这与 ancestor_ids[] 一起可以确定给定 cgroup 是否是另一个 cgroup 的后代,而无需遍历层次结构。
max_depth: 最大允许下降树深度
nr_descendants: 跟踪可见和dying descent cgroup 的总数。dying cgroups 是被用户删除的 cgroups,但由于其他人持有引用而仍然存在。max_descendants 是允许的最大descent cgroup 数。
nr_descendants 和 nr_dying_descendants 受 cgroup_mutex 和 css_set_lock 保护。读它们时可以持有 cgroup_mutex 和 css_set_lock 中的任何一个;而写,两个锁都应该被持有。
nr_populated_csets: 与此 cgroup 关联的每个非空 css_set 都向 nr_populated_csets 贡献一个。如果此 cgroup 没有任何任务,则计数为零。所有具有非零 nr_populated_csets 和/或 nr_populated_children 的子节点
根据其类型向 nr_populated_domain_children 或 nr_populated_threaded_children 贡献一个。 如果子树中所有类型的 cgroup 都没有任何任务,则每个计数器为零。
nr_threaded_children: 实时线程子 cgroups
subtree_control: 在child cgroup 上启用的子系统的位掩码。 ->subtree_control 是通过“cgroup.subtree_control”配置的,而 ->child_ss_mask 是有效的,可以启用更多子系统。如果在 ->subtree_control 中启用了,则控制器旋钮可用。
subsys[]: 每个注册子系统的私有指针
root: init_cgroup_root()中 cgrp_dfl_root.cgrp.root 指向 cgrp_dfl_root
cset_links: 指向 css_sets 的 cgrp_cset_links 列表,其中包含此 cgroup 中的任务。 受 css_set_lock 保护。
e_csets[]: 在默认层次结构中,禁用了某些 susbsys 的 cgroup 的 css_set 将指向与启用了 subsys 的最近祖先相关联的 css。下面列出了所有指向给定子系统的 cgroup 的 css 的 css_sets。
dom_cgrp: 如果 !threaded,self。如果线程化,它指向最近的域祖先。 在线程子树中,cgroup 不受进程粒度和无内部任务约束。与特定任务无关的域级资源消耗计入 dom_cgrp。
rstat_cpu: 每个 CPU 的递归资源统计。def_cgrp_group 定义时初始化为 &cgrp_dfl_root_rstat_cpu
last_bstat: cgroup基础资源统计
pidlists: pidlists 列表,每个命名空间最多两个(一个用于 procs,一个用于tasks),按需创建。
offline_waitq: 用于等待csses下线
release_agent_work: 用于调度 release agent
psi: 用于跟踪pressure stalls
bpf: 用于存放eBPF程序
congestion_count: 用于判断此 cgroup 上是否存在块拥塞。
freezer: 用于存储内部freezer状态
ancestor_ids[]: 每个级别的祖先的 ID,包括自己。
6. struct cgroup_root
struct cgroup_root { struct kernfs_root *kf_root; unsigned int subsys_mask; int hierarchy_id; struct cgroup cgrp; u64 cgrp_ancestor_id_storage; /* for cgrp->ancestor_ids[0] */ atomic_t nr_cgrps; struct list_head root_list; unsigned int flags; char release_agent_path[PATH_MAX]; char name[MAX_CGROUP_ROOT_NAMELEN]; };
cgroup_root 结构代表 cgroup 层次结构的根,并且可以与 kernfs_root 相关联以形成活动层次结构。这是 cgroup core内部的,不要直接从控制器访问。
subsys_mask: 附加到此层次结构的子系统的位掩码。cgroup_init()中 cgrp_dfl_root.subsys_mask 在初始化时,已经初始化的子系统在这个mask中。
hierarchy_id: 此层次结构中保持唯一的ID。
cgrp: 根cgroup。 Root 在释放时被销毁。
nr_cgrps: 层次结构中的 cgroup 数量,仅用于 /proc/cgroups
root_list: 遍历活动层次结构的列表
flags: 特定于层次结构的标志
release_agent_path[]: 用于release notifications的路径。初始化为 ctx->release_agent.
name[]: 此层次结构的名称,可能为空。init_cgroup_root中初始化为 cxt->name。
7. struct cftype
cgroup各文件节点的定义结构,如 "cgroup.procs",主要在 cgroup_init_cftypes 中初始化。
/* * struct cftype:cgroup控制文件的处理程序定义 * * 读/写文件时: * - 要使用的 cgroup 是 file->f_path.dentry->d_parent->d_fsdata * - 文件的 'cftype' 是 file->f_path.dentry->d_fsdata */ struct cftype { char name[MAX_CFTYPE_NAME]; unsigned long private; size_t max_write_len; unsigned int flags; /* CFTYPE_* flags */ unsigned int file_offset; 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); u64 (*read_u64)(struct cgroup_subsys_state *css, struct cftype *cft); s64 (*read_s64)(struct cgroup_subsys_state *css, struct cftype *cft); 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); int (*write_u64)(struct cgroup_subsys_state *css, struct cftype *cft, u64 val); int (*write_s64)(struct cgroup_subsys_state *css, struct cftype *cft, s64 val); 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_ALLOC struct lock_class_key lockdep_key; #endif };
name[]: 按照惯例,名称应以子系统的名称开头,后跟一个句点。零长度字符串表示 cftype 数组的结尾。
max_write_len: 可以传递给写入的字符串的最大长度,不包括尾随 nul。 如果 <PAGE_SIZE-1,则假定为 PAGE_SIZE-1。
flags: cgroup_init()中 ss->dfl_cftypes 中指定的文件节点会或上 __CFTYPE_ONLY_ON_DFL。ss->legacy_cftypes 中指定的文件节点会或上 __CFTYPE_NOT_ON_DFL,若二者相同指向,就不会或上任何标志。
file_offset: 如果非零,则应包含从 css 开始到 struct cgroup_file 字段的偏移量。cgroup 会将创建的文件的句柄记录到其中。只要包含的 css 保持可访问性,就可以使用记录的句柄。
ss: 用于内部簿记的字段。 注册时自动初始化。cgroup_init_cftypes()中指向对应的子系统。
kf_ops: cgroup_init_cftypes: 若文件节点实现了.seq_start 回调就指向全局 cgroup_kf_ops,否则指向全局 cgroup_kf_single_ops。
write: write()是通用的写回调,它直接映射到 kernfs 写操作并覆盖所有其他操作。最大写入大小由 ->max_write_len 决定。使用 of_css/cft() 访问关联的 css 和 cft。
8. struct cgroup_subsys
cgroup 子系统类型。也就是一个 cgroup 子系统对应一个此结构。详见 Documentation/admin-guide/cgroup-v1/cgroups.rst
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); void (*css_rstat_flush)(struct cgroup_subsys_state *css, int cpu); int (*css_extra_stat_show)(struct seq_file *seq, struct cgroup_subsys_state *css); 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, struct css_set *cset); void (*cancel_fork)(struct task_struct *task, struct css_set *cset); void (*fork)(struct task_struct *task); void (*exit)(struct task_struct *task); void (*release)(struct task_struct *task); void (*bind)(struct cgroup_subsys_state *root_css); bool early_init:1; bool implicit_on_dfl:1; bool threaded:1; bool broken_hierarchy:1; bool warned_broken_hierarchy:1; int id; const char *name; const char *legacy_name; struct cgroup_root *root; struct idr css_idr; /* idr for css->id */ struct list_head cfts; struct cftype *dfl_cftypes; /* for the default hierarchy */ struct cftype *legacy_cftypes; /* for the legacy hierarchies */ unsigned int depends_on; };
css_alloc: 创建 cgroup_subsys_state 结构体,和 .css_free 相对。
css_online: 对此子系统调用 cgroup_init_subsys 之后调用。
can_attach: 在将task附着到cgroup之前进行检查,如果失败则停止附着过程。
cancel_attach: attach失败时调用。
attach: 将task附着到cgroup上。
can_fork: cgroup_init_subsys()中 can_fork、fork、exit、release 若有提供这四个回调函数则全局 have_xx_callback 变量对应的 ss->id 位就设为1。
fork: 将一个task写入cgroup。
exit: 将一个task移出cgroup。
bind: cgroup_init 中会调用,传参为 init_css_set.subsys[ssid]。
early_init: cgroup_init_early()中若为true就会提前调用cgroup_init_subsys(ss, true)对其初始化。
implicit_on_dfl: 如果%true,默认层次结构上的控制器不会显示在“cgroup.controllers”或“cgroup.subtree_control”中,默认层次结构上的所有 cgroup 上都会隐式启用,并绕过“无内部进程”约束. 这适用于对用户空间透明的实用程序类
型控制器。隐式控制器可以随时从默认层次结构中被盗,因此必须可以与来自先前层次结构的离线 csses 与当前 csses 共存。
threaded: 如果为 %true,则控制器在默认层次结构上支持线程模式。在线程子树中,进程粒度和无内部进程约束都被忽略,线程控制器应该能够处理它。请注意,由于默认层次结构上的所有 cgroup 上都会自动启用隐式控制器,因此它也应该是线程化的。不支持隐式 && !threaded。
broken_hierarchy: 如果为 %false,则该子系统具有适当的层次结构 - 对父 cgroup 的配置、资源统计和限制涵盖其子 cgroup 的那些。 如果 %true,层次结构支持在某些方面被破坏 - 一些子系统完全忽略层次结构,而其他子系统只实现了一半。
如果子系统损坏并且 cgroup 核心将在这种情况下发出警告消息,现在不允许创建嵌套 cgroup。 最终,所有子系统都将正确分层,这将消失。
id: 在开机时初始化。cgroup_init_early: 赋值为其在 cgroup_subsys_id 中的 id,例如cpu的就是cpu_cgrp_id
name: 在开机时初始化。cgroup_init_early: 赋值为其在cgroup_subsys_name[]中的name,例如cpu的就是"cpu"。
legacy_name: 可选,如果未设置则在启动期间自动初始化。cgroup_init_early: 若是没有初始化就是cpu_cgrp_subsys[]中的name, 例如cpu的cpu_cgrp_subsys没有初始化就是"cpu"。
root: 链接到父级,受 cgroup_lock() 保护。cgroup_init_subsys: 标记early_init=true的子系统指向 cgrp_dfl_root。
cfts: cftype 列表。 每个条目都是以零长度名称终止的数组的第一个条目。
dfl_cftypes: 自动注册的基本 cftype。 两者可以指向同一个数组。
legacy_cftypes: cgroup子系统的节点,这些节点都是在 xxx_cgrp_subsys 中定义的。
depends_on: 一个子系统可能依赖于其他子系统。 当在 cgroup 上启用此类子系统时,如果可用,则依赖的子系统将一起启用。由于依赖关系而启用的子系统在显式启用之前对用户空间不可见。这里指定了这个依赖的子系统的掩码。
9. task_struct 中和cgroup相关的部分
struct task_struct { ... #ifdef CONFIG_CGROUPS unsigned no_cgroup_migration:1; unsigned frozen:1; struct css_set __rcu *cgroups; struct list_head cg_list; #endif ... };
no_cgroup_migration: 禁止 userland 启动的 cgroup 迁移
frozen: 任务被冻结/停止(由 cgroup freezer 使用)
cgroups: 受 css_set_lock 保护的控制组信息。cgroup_init_early: 将 init_task.cgroups 初始化为 struct css_set init_css_set
cg_list: cg_list 受 css_set_lock 和 tsk->alloc_lock 保护。
三、全局变量
1. struct cgroup_root cgrp_dfl_root;
在 cgroup.c 中定义,并 EXPORT_SYMBOL_GPL(cgrp_dfl_root) 导出。其 .cgrp 是default cgroup。
2. static struct cftype cgroup_base_files[]
在 cgroup.c 中定义,指定 "cgroup.type"、"cgroup.procs" 等文件节点,在 cgroup_init() 中初始化。
3. struct cftype cgroup1_base_files[];
历史遗留的cgroup层次结构文件,在 cgroup.c 中定义,指定 "cgroup.procs"、"cgroup.clone_children"、"tasks" 等
文件节点,在 cgroup_init() 中初始化。
4. struct css_set init_css_set;
在 cgroup.c 中定义并初始化,在 cgroup_init_subsys() 中 init_css_set.subsys[ss->id] = css,指向各个子系统的
cgroup_subsys_state 结构。
5. 一些位掩码标记
have_fork_callback
have_exit_callback
have_release_callback
have_canfork_callback
位掩码,标记各个cgroup子系统是否实现 fork、exit、release、canfork 回调函数,各个子系统分别对应 ss->id 位。
posted on 2023-03-03 11:34 Hello-World3 阅读(628) 评论(0) 编辑 收藏 举报