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_CPUSETS
cpu
: 控制CPU
分组调度,用于控制不同分组的调度时间片分配,确保高优先级的任务可以得到更多的时间片,对应的内核配置为CONFIG_CGROUP_SCHED
cpuacct
: 用于控制不同分组的CPU
使用状态统计,可以看到各个分组的CPU
调度与使用状态数据,要使用cpuacct
需要开启内核配置CONFIG_CGROUP_CPUACCT
blkio
: 用于控制不同分组的磁盘IO
资源的使用,比如保证前台的应用IO
优先级与带宽,减少后台应用对系统IO
的抢占,要使用blkio
需要开启内核配置CONFIG_BLK_CGROUP
memcg
: 控制不同分组的内存分配与使用,比如限制某些进程的内存使用量;比如在虚拟机的场景,限制虚拟机总的内存使用量;memcg
对应的内存配置CONFIG_MEMCG
freezer
: 冻结分组子系统,通常用于进程的冻结控制,比如系统资源紧张时,主动冻结后台的某些任务,减少系统资源压力,要使用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
响应延迟