cgroups(Control Groups)即控制分组,是Linux中的一种进程资源分组访问控制机制,用于将系统中的进程划分为不同的分组(形成一种树状层级的结构),利用不同的分组可以实现对各个进程的资源使用,如CPU、IO、内存、网络等系统资源进行优先级管理,可以确保在系统资源紧张的情况下,高优先级的进程可以获得更多的系统资源。简单来说,通过cgroups,我们可以实现对系统资源的分配、访问优先级、访问限制以及管理、监控等更精细的控制,从而提升系统的性能。本文主要介绍Android系统如何利用cgroups来改善系统性能,主要分为以下几个部分:
- 简单介绍
cgroup的实现原理 - Android中的
cgroup分组管理策略 - 如何利用
cgroup优化Android系统性能
cgroup的实现原理
Linux内核在初始化时,会初始化cgroup相关的配置,创建一个根cgroup分组,并注册一个虚拟的文件系统挂载到/sys/fs/cgroup目录下,这样用户空间执行mount之后就可以通过这些接口进行相关的操作。
1 |
|
目前Linux内核中常用的cgroup有如下几种:
cpuset: 控制CPU核的分组,可以将指定的CPU核心分配到某个cgroup中,从而控制系统中的CPU资源的使用;要使用cpuset需要开启内核配置CONFIG_CPUSETScpu: 控制CPU分组调度,用于控制不同分组的调度时间片分配,确保高优先级的任务可以得到更多的时间片,对应的内核配置为CONFIG_CGROUP_SCHEDcpuacct: 用于控制不同分组的CPU使用状态统计,可以看到各个分组的CPU调度与使用状态数据,要使用cpuacct需要开启内核配置CONFIG_CGROUP_CPUACCTblkio: 用于控制不同分组的磁盘IO资源的使用,比如保证前台的应用IO优先级与带宽,减少后台应用对系统IO的抢占,要使用blkio需要开启内核配置CONFIG_BLK_CGROUPmemcg: 控制不同分组的内存分配与使用,比如限制某些进程的内存使用量;比如虚拟化的场景,限制客户机总的内存使用量;memcg对应的内存配置CONFIG_MEMCGfreezer: 冻结分组子系统,通常用于进程的冻结控制,比如系统资源紧张时,主动冻结后台的某些任务,减少系统资源压力,要使用freezer需要开启内核配置CONFIG_FREEZER
Android的cgroup配置都放在描述文件cgroups.json(/system/core/libprocessgroup/profiles/)中进行配置,init进程启动的时候会主动读取该配置文件,然后将各个分组控制器挂载到/dev/xxx对应的节点下,比如cpu分组控制器对应的目录为/dev/cpuctl; cpuset对应的目录为/dev/cpuset; memory对应的目录为/dev/memcg.
1 |
|
除此之外,init初始化时,Android会创建各种cgroup分组,然后通过rc配置或者Framework的接口设置各个进程所在的分组状态。接下来我们就来详细看看Android对应的cgroup分组管理策略。
Android中的cgroup分组管理策略
Android为了确保前台应用的资源使用,减少后台应用对资源的抢占,保证关键任务的执行与调度,增加了好几个进程分组:
foreground: 前台进程分组,大部分的应用都属于这个分组,包括系统服务、应用、桌面、系统UI等。background: 后台进程分组,系统的一些常驻后台进程,如logd等可以放在这个分组中system-background: 系统服务进程分组,Android一些关键系统服务可以放入该分组,如update_engine,traced_perf,system_server等都放在该分组中top-app: 系统交互进程分组,正在执行的系统交互的可见应用都会放入该分组,确保前台交互应用的资源优先级camera-daemon: 摄像头进程分组,摄像头相关的核心服务放入该进程,确保使用摄像头的进程资源分配的优先级
Android系统提供了task_profiles.json(/system/core/libprocessgroup/profiles/)任务配置文件描述进程或者线程要执行的特定操作;每组操作都与一个配件名称相关联,并且可以通过函数SetTaskProfiles/SetProcessProfiles进行设置:
例如,原生的task_profiles.json文件:
1 |
|
任务配置文件主要包括两个部分:Attributes和Profiles。Attributes部分描述了如何配置控制组的属性,主要包含如下内容:
name字段: 制定Attribute的名称Controller字段: 按照名称引用cgroups.json文件的cgroup控制器File字段:相应控制器下的特定文件
Attributes是任务配置文件定义中的引用;在任务配置文件之外,仅当框架需要直接访问相应文件且无法使用任务配置文件抽象访问时,才应使用属性。在所有其他情况下,应该使用任务配置文件;它们可以更好地分离所需行为及其实现详情。Profiles部分使用以下字段来包含任务配置文件定义:
Name字段:定义配置文件的名称Actions部分: 列出配置文件对应执行的一组操作。每项操作包含如下几项:Name字段: 指定操作Params字段: 指定操作的一组参数
下表是常用的受支持的操作:
| 操作 | 参数 | 说明 |
|---|---|---|
| SetTimerSlack | Slack | 定时器可宽延时间(ns) |
| SetAttribute | Name/Value | 引用Attributes部分中某一个属性的名称和值 |
| WriteFile | FilePath/Value | 文件的路径和要写入的文件值 |
| JoinCgroup | Controller/Path | 指定控制组的名称和对应的cgroup路径 |
Android12及以上的版本有一个AggregateProfiles项,包含了聚合的配置文件,每个聚合配置文件对应了一个或者多个配置文件的别名。其包含了两个部分的内容:
Name字段:定义聚合配置文件的名称Profiles字段: 聚合配置文件中包含的配置文件名称
利用cgroup优化Android系统性能
Android提供了两种方式来控制进程的cgroup分组以及对应的优先级状态,一种是通过init脚本语言命令来设置,一种是通过libprocessgroup的接口来设置:
init脚本语言提供了一个task_profiles命令来设置进程的cgroup分组状态。task_profiles命令的格式如下:
1 |
|
Android 12以下的版本也可以通过writepid来写入到对应的cgroup目录:
1 |
|
API接口设置进程的cgroup分组状态: 为了保持兼容,Android 10及更高版本保留了cutils/sched_policy.h的接口:set_cpuset_policy、set_sched_policy和get_sched_policy,但Android 10以上的版本已经将对应的接口全部移到了libprocessgroup中,因此建议使用processgroup/sched_policy.h的接口。
那么,利用Android提供的cgroup机制,我们可以做哪些方面的性能优化了?cgroup的核心是资源的分配与控制,确保系统优先级的任务得到更多资源,从这个角度出发,我们可以大致有如下几个优化的方向:
- 通过
cpu的cgroup分组管理,合理分配系统大小核心,这对于移动端大小核的异构架构来说,尤其重要。例如,将top-app,camera-daemon相关的分组绑定到大核,而backgroud/system-backgroud等分组绑定到小核,可以确保系统关键的任务得到更多资源,确保系统响应延迟 - 为了减少渲染延迟,可以适当的将
SurfaceFlinger相关的线程与服务都尽量绑定到大核上,从而提升系统渲染的帧率,减少卡顿、丢帧 - 通过
blkio分组,可以用来控制前后台的I/O资源使用,在系统高负载时限制后台的I/O资源使用,从而提高前台应用的I/O响应延迟