Android/Linux下CGroup框架分析及其使用
1 cgroup介绍
CGroup是control group的简称,它为Linux kernel提供一种任务聚集和划分的机制,可以限制、记录、隔离进程组(process groups)所使用的资源(cpu、memory、I/O等)。CGroup也是LXC为实现虚拟化所使用的资源管理手段。CGroup本身是提供将进程进行分组化管理的功能和接口的基础结构,I/O或内存的分配控制等具体的资源管理功能是通过这个功能来实现的。这些具体的资源管理功能称为CGroup子系统。
CGroup子系统包含如下:
子系统 |
主要功能 |
blkio |
设置限制每个块设备的输入输出控制。 |
cpu |
使用调度程序为CGroup任务提供CPU的访问。 |
cpuacct |
产生CGroup任务的CPU资源报告,CPU Accounting Controller。 |
cpuset |
如果是多核CPU,这个子系统就会为CGroup任务分配单独的CPU和内存。 |
devices |
允许或拒绝CGroup任务对设备的访问。 |
freezer |
暂停或恢复CGroup任务。 |
hugetlb |
允许限制CGroup 的HubeTLB使用 |
memory |
设置每个CGroup的内存限制以及产生内存资源报告。 |
net_cls |
标记每个网络包以供CGroup方便使用。 |
net_prio |
提供接口以供动态调节程序的网络传输优先级。 |
perf_event |
增加了对没group的检测跟踪的能力,即可以检测属于某个特定的group的所有线程以及运行在特定CPU上的线程。 |
1.1 subsystem、task、hierarchy介绍及其关系
任务task,在CGroup中,任务就是系统的一个进程。
CGroup就是一组按照某种标准进行划分的进程。CGroup中的资源控制都是以control group为单位实现。一个进程可以加入到某个控制族群,也可以一个进程组迁移到另一个控制族群。
subusystem即子系统,是CGroup中可添加删除的模块,是CGroup下对资源进行的一种封装,比如内存资源、CPU资源、网络资源等。一个子系统就是一个资源控制器,鼻子痛必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级后,这个层级上的所有控制组全都受到这个子系统的控制。
hierarchy可以认为是一系列cgroups的集合。hierarchy是这个集合的根。
相互关系:
每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup,此 cgroup 在创建层级时自动创建,后面在该层级中创建的 cgroup 都是此
cgroup 的后代)的初始成员;
一个子系统最多只能附加到一个层级;
一个层级可以附加多个子系统;
一个任务可以是多个 cgroup 的成员,但是这些 cgroup 必须在不同的层级;
系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的 cgroup。
图表 1CGroup层级图
1.2 mount介绍
更详细信息参考:
linux内核mount系统调用源码分析http://blog.csdn.net/wugj03/article/details/41958029/
linux系统调用mount全过程分析http://blog.csdn.net/skyflying2012/article/details/9748133
在系统启动时,mount需要的CGroup子系统:
mount cgroup none /dev/cpuctl cpu |
在用户空间将mount命令转换成系统调用sys_mount:
asmlinkage long sys_mount(char __user *dev_name, char __user *dir_name, char __user *type, unsigned long flags, void __user *data); |
从sys_mount到具体文件系统的.mount调用流程如下:
sys_mount(fs/namespace.c) -->do_mount(kernel_dev, dir_name, kernel_type, flags, (void *)data_pate) -->do_new_mount (&path, type_page, flags, mnt_flags,dev_name, data_page) --> vfs_kern_mount(type, flags, name, data) --> mount_fs(type, flags, name, data) --> type->mount(type, flags, name, data) --> cgroup_mount(fs_type, flags, unused_dev_name, data) |
struct file_system_type { const char *name; 文件系统名称 int fs_flags; … struct dentry *(*mount) (struct file_system_type *, int, const char *, void *); 挂载文件系统的调用。 void (*kill_sb) (struct super_block *); 卸载文件系统的调用 struct module *owner; VFS内部调用时使用 struct file_system_type * next; struct hlist_head fs_supers; struct lock_class_key s_lock_key; struct lock_class_key s_umount_key; struct lock_class_key s_vfs_rename_key; struct lock_class_key s_writers_key[SB_FREEZE_LEVELS]; struct lock_class_key i_lock_key; struct lock_class_key i_mutex_key; struct lock_class_key i_mutex_dir_key; } |
2 代码分析
2.1 核心
2.1.1 框架结构图
CGoup核心主要创建一系列sysfs文件,用户空间可以通过这些节点控制CGroup各子系统行为。各子系统模块根据参数,在执行过程中或调度进程道不同CPU上,或控制CPU占用时间,或控制IO带宽等等。另,在每个进程的proc文件系统中都有一个cgroup,显示该进程对应的CGroup各子系统信息。
如果CGroup需要early_init,start_kernel调用cgroup_init_early在系统启动时进行CGroup初始化。
int __init cgroup_init_early(void) { static struct cgroup_sb_opts __initdata opts; struct cgroup_subsys *ss; int i; 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; ss->name = cgroup_subsys_name[i]; if (ss->early_init) cgroup_init_subsys(ss, true); } return 0; } |
CGroup的起点是start_kernel->cgroup_init,进入CGroup的初始化,主要注册cgroup文件系统和创建、proc文件,初始化不需要early_init的子系统。
int __init cgroup_init(void) { … for_each_subsys(ss, ssid) { 遍历所有子系统,初始化,根据属性配置不同文件节点 if (ss->early_init) { early_init是能的已经在cgroup_init_early中初始化 struct cgroup_subsys_state *css = init_css_set.subsys[ss->id]; css->id = cgroup_idr_alloc(&ss->css_idr, css, 1, 2, GFP_KERNEL); BUG_ON(css->id < 0); } else { cgroup_init_subsys(ss, false); } list_add_tail(&init_css_set.e_cset_node[ssid], &cgrp_dfl_root.cgrp.e_csets[ssid]); cgrp_dfl_root.subsys_mask |= 1 << ss->id; if (cgroup_legacy_files_on_dfl && !ss->dfl_cftypes) ss->dfl_cftypes = ss->legacy_cftypes; if (!ss->dfl_cftypes) cgrp_dfl_root_inhibit_ss_mask |= 1 << ss->id; 根据不同类型,创建不同节点列表。 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)); } if (ss->bind) ss->bind(init_css_set.subsys[ssid]); } err = sysfs_create_mount_point(fs_kobj, "cgroup"); 创建挂载节点/sys/fs/cgroup if (err) return err; err = register_filesystem(&cgroup_fs_type); 注册cgroup_fs_type文件系统。 if (err < 0) { sysfs_remove_mount_point(fs_kobj, "cgroup"); return err; } proc_create("cgroups", 0, NULL, &proc_cgroupstats_operations); 创建/proc/cgroups节点 return 0; } |
/proc/<pid>/cgroup指向proc_cgroup_show,用于显示此pid所对应的cgroup路径信息:
static const struct pid_entry tid_base_stuff[] = { … #ifdef CONFIG_CGROUPS ONE("cgroup", S_IRUGO, proc_cgroup_show), #endif … } |
CGroup的debug接口在CONFIG_CGROUP_DEBUG使能后打开:
struct cgroup_subsys debug_cgrp_subsys = { .css_alloc = debug_css_alloc, .css_free = debug_css_free, .legacy_cftypes = debug_files, }; |
cgroup_fs_type作为mount命令的参数,其中cgroup_mount为各subsystem进行mount工作。
2.1.2 核心结构体分析
struct cgroup_subsys { struct cgroup_subsys_state *(*css_alloc)(struct cgroup_subsys_state *parent_css); 创建cgroup_subsys_state结构体,和css_free相对。 int (*css_online)(struct cgroup_subsys_state *css); 在subsystem相关资源分配完之后,进行online相关操作,和css_offline相对。 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_e_css_changed)(struct cgroup_subsys_state *css); int (*allow_attach)(struct cgroup_subsys_state *css, struct cgroup_taskset *tset); int (*can_attach)(struct cgroup_subsys_state *css, struct cgroup_taskset *tset); void (*cancel_attach)(struct cgroup_subsys_state *css, struct cgroup_taskset *tset); void (*attach)(struct cgroup_subsys_state *css, struct cgroup_taskset *tset); allow_attach和can_attach都是在将task附着到cgroup之前进行检查,如果失败则停止附着过程。 cancel_attach是在can_attache成功之后,但是attache又失败的情况下调用。 void (*fork)(struct task_struct *task); 将一个task写入cgroup void (*exit)(struct cgroup_subsys_state *css, 将一个task移出cgroup struct cgroup_subsys_state *old_css, struct task_struct *task); void (*bind)(struct cgroup_subsys_state *root_css); int disabled; int early_init; 是否需要进行early_init,如果需要会在cgroup_init_early中提前进行初始化。 bool broken_hierarchy; bool warned_broken_hierarchy; /* the following two fields are initialized automtically during boot */ int id; #define MAX_CGROUP_TYPE_NAMELEN 32 const char *name; /* link to parent, protected by cgroup_lock() */ struct cgroup_root *root; /* idr for css->id */ struct idr css_idr; /* * List of cftypes. Each entry is the first entry of an array * terminated by zero length name. */ struct list_head cfts; /* * Base cftypes which are automatically registered. The two can * point to the same array. */ struct cftype *dfl_cftypes; /* for the default hierarchy */ struct cftype *legacy_cftypes; /* for the legacy hierarchies */ CGroup子系统的节点,这些节点都是在xxx_cgrp_subsys中定义的。 /* * A subsystem may depend on other subsystems. When such subsystem * is enabled on a cgroup, the depended-upon subsystems are enabled * together if available. Subsystems enabled due to dependency are * not visible to userland until explicitly enabled. The following * specifies the mask of subsystems that this one depends on. */ unsigned int depends_on; } |
legacy hierarchy类型的接口文件:
static struct cftype cgroup_legacy_base_files[] = { { .name = "cgroup.procs", … }, { .name = "cgroup.clone_children", .read_u64 = cgroup_clone_children_read, .write_u64 = cgroup_clone_children_write, }, { .name = "cgroup.sane_behavior", .flags = CFTYPE_ONLY_ON_ROOT, .seq_show = cgroup_sane_behavior_show, }, { .name = "tasks", … }, { .name = "notify_on_release", .read_u64 = cgroup_read_notify_on_release, .write_u64 = cgroup_write_notify_on_release, }, { .name = "release_agent", … }, { } /* terminate */ } |
cgroup.procs:属于该分组的PID列表,仅包括多线程进程的线程leader的TID,这点和tasks不同。
cgroup.clone_children:仅适用于cpuset。如果使能,创建子cpuset时,就会拷贝父cpust的配置。
cgroup.sane_behavior:未实现。
tasks:属于该分组的线程TID列表。
notify_on_release:设置是否执行release_agent,为1时使能。
release_agent:删除分组时执行的命令,这个文件只存在于根分组。
2.2 子系统
CGroup子系统都定义在cgroup_subsys.h中;kernel/cgroup.c包含此头文件,定义了两个结构体数组cgroup_subsys和cgroup_subsys_name。CGroup core对于各子系统的引用都是通过cgroup_subsys这个数组。
/* generate an array of cgroup subsystem pointers */ #define SUBSYS(_x) [_x ## _cgrp_id] = &_x ## _cgrp_subsys, static struct cgroup_subsys *cgroup_subsys[] = { #include <linux/cgroup_subsys.h> }; #undef SUBSYS /* array of cgroup subsystem names */ #define SUBSYS(_x) [_x ## _cgrp_id] = #_x, static const char *cgroup_subsys_name[] = { #include <linux/cgroup_subsys.h> }; #undef SUBSYS |
2.2.1 blkio
block/blk-cgroup.c中定义了blkio子系统结构体:
struct cgroup_subsys io_cgrp_subsys = { |
由于默认init.rc没有加载,可以添加创建相关以便研究:
mkdir /dev/blkio 0700 root system |
由于cfq_iosched.c中cfq_init注册了blkcg_policy_cfq,配置节点就变成如下列表。
CFQ,Complete Fairness Queueing。CFQ调度器主要目的提供一种进程间公平分配磁盘带宽的调度方法。
深度阅读:http://lxr.free-electrons.com/source/Documentation/block/cfq-iosched.txt
blkio.io_merged |
2.2.2 cpu
kernel/sched/core.c定义了cpu_cgrp_subsys结构体,需要进行early_init。
struct cgroup_subsys cpu_cgrp_subsys = { .css_alloc = cpu_cgroup_css_alloc, .css_free = cpu_cgroup_css_free, .css_online = cpu_cgroup_css_online, .css_offline = cpu_cgroup_css_offline, .fork = cpu_cgroup_fork, .can_attach = cpu_cgroup_can_attach, .attach = cpu_cgroup_attach, .allow_attach = subsys_cgroup_allow_attach, .exit = cpu_cgroup_exit, .legacy_cftypes = cpu_files, .early_init = 1, }; |
这里重点分析一下cpu_files,在实际使用中打开了shares/rt_runtime_us/rt_perios_us。
其中shares是针对CFS进程的,rt_runtime_us/rt_perios_us是针对RT进程的。
另外cfs_perios_us/cfs_quota_us是设置CFS进程占用的CPU带宽。
static struct cftype cpu_files[] = { |
下面是Android一个配置,根目录下面对应的是前台应用,bg_non_interactive对应的是后台应用:
/dev/cpuctl/cpu.shares 1024 /dev/cpuctl/cpu.rt_runtime_us 950000 /dev/cpuctl/cpu.rt_period_us 1000000 /dev/cpuctl/bg_non_interactive/cpu.shares 52 /dev/cpuctl/bg_non_interactive/cpu.rt_runtime_us 10000 /dev/cpuctl/bg_non_interactive/cpu.rt_period_us 1000000 |
cpu.shares:保存了整数值,用来设置cgroup分组任务获得CPU时间的相对值。举例来说,cgroup A和cgroup B的cpu.share值都是1024,那么cgroup A 与cgroup B中的任务分配到的CPU时间相同,如果cgroup C的cpu.share为512,那么cgroup C中的任务获得的CPU时间是A或B的一半。
从上面的数据可以看出,默认分组与bg_non_interactive分组cpu.share值相比接近于20:1。由于Android中只有这两个cgroup,也就是说默认分组中的应用可以利用95%的CPU,而处于bg_non_interactive分组中的应用则只能获得5%的CPU利用率。
52/(1024+52)=4.8%
cpu.rt_runtime_us:主要用来设置cgroup获得CPU资源的周期,单位为微妙。
cpu.rt_period_us:主要是用来设置cgroup中的任务可以最长获得CPU资源的时间,单位为微秒。设定这个值可以访问某个cgroup独占CPU资源。最长的获取CPU资源时间取决于逻辑CPU的数量。比如cpu.rt_runtime_us设置为200000(0.2秒),cpu.rt_period_us设置为1000000(1秒)。在单个逻辑CPU上的获得时间为每秒为0.2秒。 2个逻辑CPU,获得的时间则是0.4秒。
从上面数据可以看出,默认分组中单个逻辑CPU下每一秒内可以获得0.95秒执行时间。bg_non_interactive分组下单个逻辑CPU下每一秒内可以获得0.01秒。
疑问点:在shares和rt_period_us/cfs_period_us之间时间究竟如何分配?是先按照shares分组分配CPU时间,然后组内优先RT,剩下来给CFS?还是先RT进程按照shares分配,剩下来给CFS按照shares分配。分配时间shares在先还是RT/CFS之间在先?
cpu.cfs_runtime_us
cpu.cfs_quota_us
一个测试脚本:
#!/usr/bin/env python # coding=utf-8 i = 0 while True: i = i + 1 |
1.执行loop.py,python loop.py &。
2.top监控,可以看出CUP占用率在100%。
3.echo pid > tasks,将loop.py进程加入cpu组。
4.echo 10000 > cpu.cfs_quota_us,在cpu.cfs_period_us为100000的情况下占用率应该为10%.
5.下面依次为5000、50000对应占用率为5%、50%的情况。
2.2.3 cpuacct
kernel/sched/cpuacct.c中定义cpuacct_cgrp_subsys子系统结构体:
struct cgroup_subsys cpuacct_cgrp_subsys = { |
其中legacy_cftypes的files是cpuacct子系统的精髓:
static struct cftype files[] = { |
要理解每个统计信息的含义就绕不开struct cpuacct这个结构体。
usage:是所有usage_percpu之和。
usage_percpu:是每个CPU的使用量。
stat:分别统计user和system两种类型的,user对应CPUTIME_USER、CPUTIME_NICE,system对应CPUTIME_SYSTEM、CPUTIME_IRQ、CPUTIME_SOFTIRQ。
struct cpuacct { enum cpu_usage_stat { |
下面HiKey的一个瞬间的cpuacct值:
cpuacct.stat |
user 2312 system 2662 |
我们将其转换成每个CPU用量的百分比,结合cpuset的设置。可以得出结论:
cpu0的任务最重,所有类型的进程都可能在cpu0上调度。,cluster0要比cluster1更多的被使用。
cpu7最少被使用,因为只有top-app才会使用。
/dev/cpuset/cpus 0-7 /dev/cpuset/background/cpus 0 /dev/cpuset/foreground/cpus 0-6 /dev/cpuset/system-background/cpus 0-3 /dev/cpuset/top-app/cpus 0-7 |
2.2.4 cpuset
cpuset是一个用来分配限制CPU和Memory资源的CGroup子系统。cpuset使用sched_setaffinity系统调用来设置tasks的CPU亲和性,使用mbind和set_mempolicy包含Memory策略中的Memory Nodes。调度器不会在cpuset之外的CPU上面调度tasks,页分配器也不会在mems_allowed之外的内存中分配。
cpuset提供了一种灵活配置CPU和Memory资源的机制。Linux中已经有配置CPU资源的cpu子系统和Memory资源的memory子系统。
kernel/cpuset.c中定义了子系统cpuset结构体如下:
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, }; |
cpu_exclusive cpu资源是否专用? cpus 当前cpuset的CPU列表。 effective_cpus 有效的CPU列表 effective_mems 有效的memory mem_exclusive memory资源是否专用? mem_hardwall memory_migrate 如果置位,则将页面移到cpusets节点 memory_pressure 测量当前cpuset的paging压力 memory_spread_page if set, spread page cache evenly on allowed nodes memory_spread_slab if set, spread slab cache evenly on allowed nodes mems 当前cpuset的Memory Nodes列表 sched_load_balance 当前cpuset是否进行负载均衡 sched_relax_domain_level the searching range when migrating tasks |
如果设置了cpu/memory专用,除了直接父子,其他cpuset不可以使用相应的CPU或者Memory Nodes。
其他项的详细解释见:Documentation/cgroups/cpust.txt。
cpuset结构体中设置的cpus_allowed、mems_allowed、effective_cpus、effective_mems都会在写入cpus、mems节点是更行到当前cpuset下的task相关的。
其他情况还包括CPU hotplug的时候动态更新cpus_allowed信息。
cpuset_write_resmask -->update_cpumask -->update_nodemask -->update_cpumasks_hier -->update_nodemasks_hier -->update_tasks_cpumask 遍历当前cpuset下所有的task的cpus_allowed -->update_tasks_nodemask 更新当前cpuset下所有task的mems_allowed -->set_cpus_allowed_ptr 将cpuset的cpumask赋给task->cpus_allowed,将task转移到合适的CPU;如果CPU被拔出,则将其迁移到其它被允许的CPU上。 --> cpuset_migrate_mm 将memory区域从一个node迁移到另一个 --> do_migrate_pages 在两个node之间移动页 |
struct cpuset { struct cgroup_subsys_state css; unsigned long flags; /* "unsigned long" so bitops work */ /* user-configured CPUs and Memory Nodes allow to tasks */ cpumask_var_t cpus_allowed; 和task_struct->cpus_allowed相对应 nodemask_t mems_allowed; 和task_struct->mems_allowed相对应 /* effective CPUs and Memory Nodes allow to tasks */ cpumask_var_t effective_cpus; nodemask_t effective_mems; default hierarchy:effective_mask=configured_mask&parent’s effective_mask legacy hierarchy: user-configured masks = effective masks nodemask_t old_mems_allowed; struct fmeter fmeter; /* memory_pressure filter */ int attach_in_progress; /* for custom sched domain */ int relax_domain_level; } |
cpuset在Android的应用主要差异就是不同组配置不同的cpus,根据进程类型细分。
可以看出优先级越高的进程可以占用的cpu越多。
/dev/cpuset/cpus 0-7 /dev/cpuset/background/cpus 0 /dev/cpuset/foreground/cpus 0-6 /dev/cpuset/system-background/cpus 0-3 /dev/cpuset/top-app/cpus 0-7 |
只有根节点的mem_exclusive使能,其他都未使能。
/dev/cpuset/mem_exclusive 1 /dev/cpuset/background/mem_exclusive 0 /dev/cpuset/foreground/mem_exclusive 0 /dev/cpuset/system-background/mem_exclusive 0 /dev/cpuset/top-app/mem_exclusive 0 |
2.2.5 devices
security/device_cgroup.c定义devices子系统结构体:
struct cgroup_subsys devices_cgrp_subsys = { |
2.2.6 hugetlb
mm/hugetlb_cgroup.c
struct cgroup_subsys hugetlb_cgrp_subsys
2.2.7 memory
mm/memcontrol.c中定义了memory子系统memory_cgrp_subsys如下:
struct cgroup_subsys memory_cgrp_subsys = { .css_alloc = mem_cgroup_css_alloc, .css_online = mem_cgroup_css_online, .css_offline = mem_cgroup_css_offline, .css_free = mem_cgroup_css_free, .css_reset = mem_cgroup_css_reset, .can_attach = mem_cgroup_can_attach, .cancel_attach = mem_cgroup_cancel_attach, .attach = mem_cgroup_move_task, .allow_attach = mem_cgroup_allow_attach, .bind = mem_cgroup_bind, .dfl_cftypes = memory_files, .legacy_cftypes = mem_cgroup_legacy_files, .early_init = 0, }; |
memory_files和mem_cgroup_legacy_files的解释如下:
cgroup.event_control event_fd的接口 memory.failcnt 显示内存(进程内存+页面缓存) 达到限制值的次数 memory.force_empty 强制释放分配给分组的内存 memory.kmem.failcnt 显示内存(进程内存+页面缓存)+交换区到达限制值的次数 memory.kmem.limit_in_bytes memory.kmem.max_usage_in_bytes 显示记录的内存(进程内存+页面缓存)+交换区使用量的最大值 memory.kmem.slabinfo memory.kmem.tcp.failcnt memory.kmem.tcp.limit_in_bytes memory.kmem.tcp.max_usage_in_bytes memory.kmem.tcp.usage_in_bytes memory.kmem.usage_in_bytes memory.limit_in_bytes 显示当前内存(进程内存+页面缓存)的使用量的限制值 memory.max_usage_in_bytes 显示记录的内存使用量的最大值 memory.memsw.failcnt memory.memsw.limit_in_bytes 显示当前内存(进程内存+页面缓存)+交换区使用量的限制值 memory.memsw.usage_in_bytes 显示当前内存(进程内存+页面缓存)+交换区使用量的使用值 memory.memsw.max_usage_in_bytes memory.memsw.usage_in_bytes 显示当前内存(进程内存+页面缓存)+交换区使用量 memory.move_charge_at_immigrate memory.oom_control memory.pressure_level 设置内存压力通知 memory.soft_limit_in_bytes memory.stat 输出统计信息 memory.swappiness 设置、显示针对分组的swappiness memory.usage_in_bytes 显示当前内存(进程内存+页面缓存)的使用量 memory.use_hierarchy 设置、显示层次结构的使用 |
Android下的一个实例,显示/dev/memcfg/apps并未使用:
Item | /dev/memcfg | /dev/memcfg/apps |
memory.failcnt |
0 |
0 |
两者的memory.stat如下:
/dev/memcfg | /dev/memcfg/apps |
cache 446832640 |
cache 0 |
2.2.8 net_cls
net/core/netclassid_cgroup.c
struct cgroup_subsys net_cls_cgrp_subsys
2.2.9 net_prio
net/core/netprio_cgroup.c
struct cgroup_subsys net_prio_cgrp_subsys
2.2.10 net_perf
kernel/event/core.c
struct cgroup_subsys perf_event_cgrp_subsys
2.2.11 perf_event
kernel/events/core.c
struct cgroup_subsys perf_event_cgrp_subsys
3 CGroup在Android中的应用
# Mount cgroup mount point for cpu accounting mount cgroup none /acct cpuacct |
schedtune是ARM/Linaro为了EAS新增的一个子系统,主要用来控制进程调度选择CPU以及boost触发。
这部分涉及到EAS、Android进程调度策略等相关知识,另起一篇专门介绍《Android中关于cpu/cpuset/schedtune的应用》。
https://git.linaro.org/people/john.stultz/android-dev.git
branch:remotes/origin/android-hikey-linaro-4.4-EASv5.2+aosp
# Create energy-aware scheduler tuning nodes mkdir /dev/stune mount cgroup none /dev/stune schedtune |
# root memory control cgroup, used by lmkd mkdir /dev/memcg 0700 root system mount cgroup none /dev/memcg memory |
# Create cgroup mount points for process groups mkdir /dev/cpuctl mount cgroup none /dev/cpuctl cpu |
# sets up initial cpusets for ActivityManager |
以上sysfs节点在Android中都有对应的HAL层库文件,其中cpuctl/cpuset/stune对应libcutils.so,代码在system/core/libcutils中,主要根据进程的不同分类,进行CPU、memory资源控制;memory子系统对应lmkd系统服务,代码在system/core/lmkd,主要在内存紧张情况下,杀死低优先级进程,以达到释放内存的目的。
针对libcutils.so会在《Android中关于cpu/cpuset/schedtune的应用》,lmkd会在《Android中基于CGroup的memory子系统HAL层分析-lmkd》。
posted on 2016-12-21 17:26 ArnoldLu 阅读(14242) 评论(0) 编辑 收藏 举报