Android 10 及更高版本使用帶有任務配置文件的控制組 (cgroup) 抽象層,開發人員可以使用它來描述一組(或多組)限制以應用於線程或進程。然後系統按照任務配置文件的規定動作來選擇一個或多個適當的 cgroup,通過這些 cgroup 應用限制,並且可以在不影響更高軟件層的情況下對底層 cgroup 功能集進行更改。
關於 cgroups
Cgroups 提供了一種機制,用於將任務集(包括進程、線程及其所有未來的子進程)聚合和劃分為具有特殊行為的分層組。 Android 使用 cgroups 來控制和計算系統資源,例如 CPU 和內存的使用和分配,並支持 Linux 內核cgroups v1和cgroups v2 。
安卓 9 及更低版本
在 Android 9 及更低版本中, init.rc
初始化腳本包含一組可用的 cgroup、它們的掛載點和版本。雖然這些可以更改,但 Android 框架希望基於腳本的特定位置存在一組特定的 cgroup,具有特定的版本和子組層次結構。這限制了選擇要使用的下一個 cgroup 版本或更改 cgroup 層次結構以使用新功能的能力。
Android 10 及更高版本
Android 10 及更高版本使用帶有任務配置文件的 cgroup:
- Cgroup 設置 - 開發人員在其
cgroups.json
文件中描述 cgroup 設置以定義 cgroup 集及其安裝位置和屬性。所有 cgroup 都在初始化過程的 early-init 階段掛載。 - 任務配置文件——這些提供了一種抽象,將所需的功能與其實現的細節分離。 Android 框架使用
SetTaskProfiles
和SetProcessProfiles
API 將task_profiles.json
文件中描述的任務配置文件應用到進程或線程。 (這些 API 是 Android 11 及更高版本獨有的。)
為了提供向後兼容性,舊函數set_cpuset_policy
、 set_sched_policy
和get_sched_policy
提供相同的 API 和功能,但它們的實現已被修改為使用任務配置文件。對於新的用例,AOSP 建議使用新的任務配置文件 API,而不是舊的set_sched_policy
函數。
Cgroups 描述文件
Cgroup 在<ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/
下的cgroups.json
文件中進行了描述。每個控制器都在一個小節中進行了描述,並且必須至少具有以下內容:
- 名稱,由Controller字段定義。
- 安裝路徑,由Path字段定義。
- Mode 、 UID (用戶 ID)和GID (組 ID)描述了此路徑下文件的所有者和訪問模式(都是可選的)。
- 可選屬性,設置為true讓系統忽略內核不支持掛載的 cgroup 控制器引起的掛載錯誤。
示例 cgroups.json 文件
下面的示例顯示了 cgroup v1 ( Cgroups
) 和 cgroup v2 ( Cgroups2
) 控制器及其各自路徑的描述。
{
"Cgroups": [
{
"Controller": "cpu",
"Path": "/dev/cpuctl",
"Mode": "0755",
"UID": "system",
"GID": "system"
},
{
"Controller": "memory",
"Path": "/dev/memcg",
"Mode": "0700",
"Optional": true
}
],
"Cgroups2": {
"Path": "/sys/fs/cgroup",
"Mode": "0755",
"UID": "system",
"GID": "system",
"Controllers": [
{
"Controller": "freezer",
"Path": ".",
"Mode": "0755",
"UID": "system",
"GID": "system"
}
]
}
}
此示例文件包含兩個部分, Cgroups (描述 cgroup v1 控制器)和Cgroups2 (描述 cgroup v2 控制器)。 cgroups v2 層次結構中的所有控制器都安裝在同一位置。因此, Cgroups2部分有自己的Path 、 Mode 、 UID和GID屬性來描述層次結構根的位置和屬性。 Cgroups2下控制器的Path屬性是相對於該根路徑的。在 Android 12 及更高版本中,您可以通過將其設置為true
來定義使用路徑和模式指定為"Optional"
的 cgroup 控制器。
cgroups.json
文件作為初始化過程的一部分在 early-init 階段被解析,並且 cgroups 被掛載在指定的位置。要稍後獲取 cgroup 安裝位置,請使用CgroupGetControllerPath
API 函數。
任務配置文件
task_profiles.json
文件位於<ANDROID_BUILD_TOP>/system/core/libprocessgroup/profiles/
下。使用它來描述要應用於進程或線程的一組特定操作。一組操作與配置文件名稱相關聯,該名稱在SetTaskProfiles
和SetProcessProfiles
調用中用於調用配置文件操作。
示例 task_profiles.json 文件
{
"Attributes": [
{
"Name": "MemSoftLimit",
"Controller": "memory",
"File": "memory.soft_limit_in_bytes"
},
{
"Name": "MemSwappiness",
"Controller": "memory",
"File": "memory.swappiness"
}
],
"Profiles": [
{
"Name": "MaxPerformance",
"Actions" : [
{
"Name" : "JoinCgroup",
"Params" :
{
"Controller": "schedtune",
"Path": "top-app"
}
}
]
},
{
"Name": "TimerSlackHigh",
"Actions" : [
{
"Name" : "SetTimerSlack",
"Params" :
{
"Slack": "40000000"
}
}
]
},
{
"Name": "LowMemoryUsage",
"Actions" : [
{
"Name" : "SetAttribute",
"Params" :
{
"Name" : "MemSoftLimit",
"Value" : "16MB"
}
},
{
"Name" : "SetAttribute",
"Params" :
{
"Name" : "MemSwappiness",
"Value" : "150"
}
}
]
}
]
"AggregateProfiles": [
{
"Name": "SCHED_SP_DEFAULT",
"Profiles": [ "TimerSlackHigh", "MaxPerformance" ]
},
{
"Name": "SCHED_SP_BACKGROUND",
"Profiles": [ "LowMemoryUsage" ]
}
}
將名稱分配給特定的 cgroup 文件作為屬性列表中的條目。每個條目包含以下內容:
- 名稱字段 - 指定屬性的名稱。
- 控制器字段 - 按名稱引用
cgroups.json
文件中的 cgroup 控制器。 - 文件字段 - 命名此控制器下的特定文件。
屬性是任務配置文件定義中的引用。在任務配置文件之外,僅當框架需要直接訪問這些文件並且無法使用任務配置文件抽象訪問時才使用它們。在所有其他情況下,使用任務配置文件;它們在所需行為與其實現細節之間提供了更好的解耦。
Profiles部分包含具有以下內容的任務配置文件定義:
- 名稱字段 - 定義配置文件名稱。
操作部分 - 列出應用配置文件時執行的一組操作。每個動作都有以下內容:
- 指定操作的名稱字段
- 指定動作的一組參數的參數部分
下表列出了支持的操作。
行動 | 範圍 | 描述 |
---|---|---|
SetTimerSlack | Slack | 以 ns 為單位的計時器鬆弛 |
SetAttribute | Name | 引用“屬性”部分中的屬性的名稱 | Value | 要寫入由命名屬性表示的文件的值 |
WriteFile | FilePath | 文件路徑 | Value | 要寫入文件的值 |
JoinCgroup | Controller | cgroups.json 中的 cgroup 控制器的名稱 |
Path | cgroup 控制器層次結構中的子組路徑 |
Android 12 及更高版本具有包含聚合配置文件的AggregateProfiles部分,每個配置文件都是一組一個或多個配置文件的別名。聚合配置文件定義包括以下內容:
- 名稱字段 - 指定聚合配置文件的名稱。
- 配置文件字段 - 列出聚合配置文件中包含的配置文件的名稱。
應用聚合配置文件時,也會自動應用所有包含的配置文件。聚合配置文件可以包含單個配置文件或其他聚合配置文件,只要沒有遞歸(包含自身的配置文件)。
task_profiles 初始化語言命令
Android 初始化語言中的task_profiles
命令可用於 Android 12 及更高版本,以促進特定進程的任務配置文件激活。它取代了用於在 cgroup 之間遷移進程的writepid
命令(在 Android 12 中已棄用)。 task_profiles
命令為更改底層實現提供了靈活性,而不會影響上層。在下面的示例中,這兩個命令有效地執行相同的操作:
writepid /dev/cpuctl/top-app/tasks
在 Android 12 中已棄用 - 用於將當前任務的 PID 寫入
/dev/cpuctl/top-app/tasks
文件。task_profiles MaxPerformance
將當前進程加入“cpu”控制器(
cpuctl
)下的 top-app 組,從而將進程的 PID 寫入dev/cpuctl/top-app/tasks
。
在 Android 12 及更高版本中,始終使用task_profiles
命令遷移 cgroup 層次結構中的任務。它接受一個或多個參數,代表task_profiles.json
文件中指定的配置文件的名稱。
每個 API 級別的任務配置文件
在 Android 12 及更高版本中,您可以修改或覆蓋默認cgroups.json
和task_profiles.json
文件中的定義,既可以基於 Android API 級別的更改,也可以從供應商分區進行更改。
要覆蓋基於 API 級別的定義,設備上必須存在以下文件:
pro/system/etc/task_profiles/cgroups_<API level>.json
將此用於特定於 API 級別的 cgroup。
/system/etc/task_profiles/task_profiles_<API level>.json
將此用於特定於 API 級別的配置文件。
要覆蓋來自供應商分區的定義,設備上必須存在以下文件:
-
/vendor/etc/cgroups.json
-
/vendor/etc/task_profiles.json
如果這些文件中的屬性或配置文件定義使用與默認文件中相同的名稱,則文件(API 級別或供應商級別)定義將覆蓋先前的定義。另請注意,供應商級別的定義會覆蓋 API 級別的定義。如果新定義具有新名稱,則使用新定義修改屬性或配置文件集。
Android系統按以下順序加載cgroup
和task_profile
文件:
- 默認
cgroups.json
和task_profiles.json
文件。 - API 級別特定文件(如果存在)。
- 供應商分區文件(如果存在)。
對現有 API 的更改
Android 10 及更高版本保留了函數set_cpuset_policy
、 set_sched_policy
和get_sched_policy
,而無需更改 API。但是,Android 10 將這些函數移到了libprocessgroup
中,現在它包含了所有與 cgroup 相關的功能。
儘管cutils/sched_policy.h
標頭仍然存在,但為避免破壞現有代碼,請確保新代碼包含新的processgroup/sched_policy.h
標頭。
使用這些函數的模塊應該將libprocessgroup
庫的依賴添加到它們的 makefile 中。如果模塊不使用任何其他libcutils
功能,請從 makefile 中刪除libcutils
庫依賴項。
任務配置文件 API
processgroup/processgroup.h
中的私有 API 定義如下表:
類型 | API 和定義 |
---|---|
bool | SetTaskProfiles(int tid, const std::vector & profiles) SetTaskProfiles(int tid, const std::vector & profiles) 使用其 |
bool | SetProcessProfiles(uid_t uid, pid_t pid, const std::vector & profiles) SetProcessProfiles(uid_t uid, pid_t pid, const std::vector & profiles) 使用 |
bool | CgroupGetControllerPath(const std::string& cgroup_name, std::string* path) 返回 |
bool | CgroupGetAttributePath(const std::string& attr_name, std::string* path) 返回 |
bool | CgroupGetAttributePathForTask(const std::string& attr_name, int tid, std::string* path) 返回 |
bool | UsePerAppMemcg() 返回系統是否配置為使用每個應用程序的內存 cgroup。 |